Initial import.

git-svn-id: http://omaha.googlecode.com/svn/trunk@19 e782f428-02b4-11de-a43f-ff96a2b7a9af
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..1a4b492
--- /dev/null
+++ b/VERSION
@@ -0,0 +1,10 @@
+# -*- Python -*-
+
+# 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 = 159  # 1-65535
+version_patch = 0    # 0-65535
+
+oneclick_plugin_version = '8'
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/__init__.py
diff --git a/bho/bho_dll.cc b/bho/bho_dll.cc
new file mode 100644
index 0000000..c263b3b
--- /dev/null
+++ b/bho/bho_dll.cc
@@ -0,0 +1,68 @@
+// 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
new file mode 100644
index 0000000..f2e5579
--- /dev/null
+++ b/bho/bho_dll.def
@@ -0,0 +1,23 @@
+; 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
new file mode 100644
index 0000000..a56fad1
--- /dev/null
+++ b/bho/bho_dll.idl
@@ -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.
+// ========================================================================
+//
+// 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
new file mode 100644
index 0000000..5542498
--- /dev/null
+++ b/bho/bho_dll.rc
@@ -0,0 +1,23 @@
+// 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
new file mode 100644
index 0000000..1fe543e
--- /dev/null
+++ b/bho/bho_dll.rgs
@@ -0,0 +1,28 @@
+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
new file mode 100644
index 0000000..2b96178
--- /dev/null
+++ b/bho/bho_entrypoint.cc
@@ -0,0 +1,49 @@
+// 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
new file mode 100644
index 0000000..8a2f16f
--- /dev/null
+++ b/bho/bho_entrypoint.h
@@ -0,0 +1,68 @@
+// 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
new file mode 100644
index 0000000..8c96c4d
--- /dev/null
+++ b/bho/browser_http_request.cc
@@ -0,0 +1,153 @@
+// 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
new file mode 100644
index 0000000..4a73b63
--- /dev/null
+++ b/bho/browser_http_request.h
@@ -0,0 +1,79 @@
+// 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
new file mode 100644
index 0000000..65ef1b8
--- /dev/null
+++ b/bho/build.scons
@@ -0,0 +1,202 @@
+#!/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 v in env['product_version']:
+  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' % v[0],
+          '/DVERSION_MINOR=%d' % v[1],
+          '/DVERSION_BUILD=%d' % v[2],
+          '/DVERSION_PATCH=%d' % v[3],
+          '/DVERSION_NUMBER_STRING=\\"%d.%d.%d.%d\\"' % (v[0],v[1],v[2],v[3]),
+          ],
+  )
+
+  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 + temp_env['BHO_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 + temp_env['BHO_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
new file mode 100644
index 0000000..d28a52d
--- /dev/null
+++ b/bho/resource.h
@@ -0,0 +1,25 @@
+// 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/bin/build.scons b/bin/build.scons
new file mode 100644
index 0000000..4f1b823
--- /dev/null
+++ b/bin/build.scons
@@ -0,0 +1,172 @@
+#!/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.
+# ========================================================================
+
+# Copies files from an official build directory (i.e on filer) to the checked
+# in build directory.
+#
+# We use Hammer instead of a batch script, because we want to copy the same set
+# of files used by the metainstaller, including all languages defined in
+# main.scons and the same version of the OneClick DLL.
+#
+# To run this script, execute the following from the omaha\ directory:
+#   hammer  --bin --official_build_path=<build_path>
+# where <build_path> is the location of the official build output. For example:
+#   //filer/shares/googleclient/save/builds/Omaha/1.2.133.15
+
+
+Import('env')
+
+import os
+
+from installers import get_payload_files
+from installers import update_version
+
+rel_dir = ('opt', 'dbg')[env.Bit('debug')]
+
+# Set the value of version_major, etc. in this file to the checked-in version.
+execfile(env.File(os.path.join('$MAIN_DIR/bin', rel_dir, 'VERSION')).abspath)
+
+
+def CopyFilesFromOfficialBuild(official_build_path):
+  update_version.UpdateSettingsExceptForCheckedInVersion(env,
+      oneclick_plugin_version)
+
+  # Build the meta-installer for each version.
+  first = True
+  for _ in env['product_version']:
+    if first:
+      first = False
+      prefix = ''
+    else:
+      prefix = 'TEST_'
+
+    mi_stub = '%smi_exe_stub.exe' % (prefix)
+
+    files_to_copy = get_payload_files.GetListOfPayloadFiles(
+        prefix,
+        env['ACTIVEX_FILENAME'],
+        env['BHO_FILENAME'],
+        env['languages'],
+        env['product_version'][0])
+
+    files_to_copy += [
+        mi_stub,
+        'VERSION',
+        ]
+
+    # Get absolute path to files to be replaced
+    bin_dir_abs = env.Dir('$MAIN_DIR/bin/' + rel_dir).abspath
+
+    # Check if the files to be replaced are writable, and fail if they are not.
+    unwritable_files = [file for file in files_to_copy if not
+        os.access(os.path.join(bin_dir_abs, file), os.W_OK)]
+
+    if len(unwritable_files) > 0:
+      UNWRITEABLE_ERROR = (
+          '\nThe following files are not writable. Make sure you \'g4 edit\'\n'
+          'all files in the <%s> dir:\n\t%s\n') % (bin_dir_abs,
+          '\n\t'.join(unwritable_files))
+      raise Exception(UNWRITEABLE_ERROR)
+
+    existing_files = os.listdir(bin_dir_abs)
+
+    files_to_copy.sort()
+    existing_files.sort()
+
+    has_different_files = False
+    if files_to_copy != existing_files:
+      has_different_files = True
+      print 'The set of files to copy is different from the existing files.'
+
+    input_files = [os.path.join(official_build_path, rel_dir, file)
+        for file in files_to_copy]
+
+    # Tell Hammer that, yes, we really do actually want to do what we're
+    # telling it do... by adding the target directory to the list of default
+    # targets. Otherwise, it will happily ignore the Replicate() command below.
+    Default(bin_dir_abs)
+
+    # Do the actual copy
+    replicate_results = env.Replicate(bin_dir_abs, input_files)
+
+    lines_to_echo = [
+        '.',
+        ' Copied %s files.' % len(files_to_copy),
+        '.',
+        ' *************************************************************',
+        ' *  ACTION REQUIRED  *  ACTION REQUIRED  *  ACTION REQUIRED  *',
+        ' *',
+        ' * Assuming you checked out all files in "#/bin/", verify that',
+        ' * no files are reverted (because they are unchanged) when you',
+        ' * g4 mail or g4 submit the CL. This would probably indicate',
+        ' * that something is wrong, because all files should be',
+        ' * submitted together (or else g4 delete\'d). The only exception',
+        ' * is the shell (GoogleUpdate.exe), because it may not change',
+        ' * between builds.',
+        ' *',
+        ' *  ACTION REQUIRED  *  ACTION REQUIRED  *  ACTION REQUIRED  *',
+        ' *************************************************************',
+        '.',
+        ]
+
+    if has_different_files:
+      lines_to_echo += [
+          '.',
+          ' *************************************************************',
+          ' * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING *',
+          ' *',
+          ' * The filenames copied are different than the existing files.',
+          ' * You may need to g4 add or g4 delete files.',
+          ' *',
+          ' * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING *',
+          ' *************************************************************',
+          '.',
+          ]
+
+    if not env.Bit('all'):
+      lines_to_echo += [
+          '.',
+          ' WARNING: Did you remember to copy for all build modes (use "-a")?',
+          '.',
+          ]
+
+    # Print out the messages, *only* after the files have
+    # been successfully copied.
+    env.Command(
+        # Need to have a dummy target here, and it has to be under
+        # $OBJ_ROOT, or Hammer will not execute the command.
+        target='$OBJ_ROOT/output',
+        # Need to pretend to be taking the result of the Replicate() command
+        # as a source, so Hammer knows what order to run the actions in.
+        source=replicate_results,
+        action='@echo%s' % '\n@echo'.join(lines_to_echo)
+    )
+
+
+OFFICIAL_ERROR_MESSAGE = """
+
+**** ERROR ****** ERROR **** ERROR **** ERROR ****
+Path to official build must be defined!
+Specify "--official_build_path=..." in the Hammer args.
+**************************************************
+
+"""
+
+if env.Bit('bin'):
+  if GetOption('official_build_path') is None:
+    raise Exception(OFFICIAL_ERROR_MESSAGE)
+  else:
+    CopyFilesFromOfficialBuild(GetOption('official_build_path'))
diff --git a/bin/dbg/GoogleUpdate.exe b/bin/dbg/GoogleUpdate.exe
new file mode 100644
index 0000000..8ceeb50
--- /dev/null
+++ b/bin/dbg/GoogleUpdate.exe
Binary files differ
diff --git a/bin/dbg/GoogleUpdateHelper.msi b/bin/dbg/GoogleUpdateHelper.msi
new file mode 100644
index 0000000..a74cee7
--- /dev/null
+++ b/bin/dbg/GoogleUpdateHelper.msi
Binary files differ
diff --git a/bin/dbg/GoopdateBho.dll b/bin/dbg/GoopdateBho.dll
new file mode 100644
index 0000000..4fc8334
--- /dev/null
+++ b/bin/dbg/GoopdateBho.dll
Binary files differ
diff --git a/bin/dbg/VERSION b/bin/dbg/VERSION
new file mode 100644
index 0000000..dd7a11e
--- /dev/null
+++ b/bin/dbg/VERSION
@@ -0,0 +1,10 @@
+# -*- Python -*-
+
+# 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 = 141  # 1-65535
+version_patch = 5    # 0-65535
+
+oneclick_plugin_version = '7'
diff --git a/bin/dbg/goopdate.dll b/bin/dbg/goopdate.dll
new file mode 100644
index 0000000..286f228
--- /dev/null
+++ b/bin/dbg/goopdate.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ar.dll b/bin/dbg/goopdateres_ar.dll
new file mode 100644
index 0000000..5f0d977
--- /dev/null
+++ b/bin/dbg/goopdateres_ar.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_bg.dll b/bin/dbg/goopdateres_bg.dll
new file mode 100644
index 0000000..edcc824
--- /dev/null
+++ b/bin/dbg/goopdateres_bg.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_bn.dll b/bin/dbg/goopdateres_bn.dll
new file mode 100644
index 0000000..d74f290
--- /dev/null
+++ b/bin/dbg/goopdateres_bn.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ca.dll b/bin/dbg/goopdateres_ca.dll
new file mode 100644
index 0000000..bf616e5
--- /dev/null
+++ b/bin/dbg/goopdateres_ca.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_cs.dll b/bin/dbg/goopdateres_cs.dll
new file mode 100644
index 0000000..586a02a
--- /dev/null
+++ b/bin/dbg/goopdateres_cs.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_da.dll b/bin/dbg/goopdateres_da.dll
new file mode 100644
index 0000000..c784b69
--- /dev/null
+++ b/bin/dbg/goopdateres_da.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_de.dll b/bin/dbg/goopdateres_de.dll
new file mode 100644
index 0000000..f3c57d1
--- /dev/null
+++ b/bin/dbg/goopdateres_de.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_el.dll b/bin/dbg/goopdateres_el.dll
new file mode 100644
index 0000000..3228644
--- /dev/null
+++ b/bin/dbg/goopdateres_el.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_en-GB.dll b/bin/dbg/goopdateres_en-GB.dll
new file mode 100644
index 0000000..95b386c
--- /dev/null
+++ b/bin/dbg/goopdateres_en-GB.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_en.dll b/bin/dbg/goopdateres_en.dll
new file mode 100644
index 0000000..ccfb959
--- /dev/null
+++ b/bin/dbg/goopdateres_en.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_es-419.dll b/bin/dbg/goopdateres_es-419.dll
new file mode 100644
index 0000000..f1b1f26
--- /dev/null
+++ b/bin/dbg/goopdateres_es-419.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_es.dll b/bin/dbg/goopdateres_es.dll
new file mode 100644
index 0000000..5d436f2
--- /dev/null
+++ b/bin/dbg/goopdateres_es.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_et.dll b/bin/dbg/goopdateres_et.dll
new file mode 100644
index 0000000..a24cae7
--- /dev/null
+++ b/bin/dbg/goopdateres_et.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_fa.dll b/bin/dbg/goopdateres_fa.dll
new file mode 100644
index 0000000..2620962
--- /dev/null
+++ b/bin/dbg/goopdateres_fa.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_fi.dll b/bin/dbg/goopdateres_fi.dll
new file mode 100644
index 0000000..8996d82
--- /dev/null
+++ b/bin/dbg/goopdateres_fi.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_fil.dll b/bin/dbg/goopdateres_fil.dll
new file mode 100644
index 0000000..64cebee
--- /dev/null
+++ b/bin/dbg/goopdateres_fil.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_fr.dll b/bin/dbg/goopdateres_fr.dll
new file mode 100644
index 0000000..446c755
--- /dev/null
+++ b/bin/dbg/goopdateres_fr.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_gu.dll b/bin/dbg/goopdateres_gu.dll
new file mode 100644
index 0000000..2c4996a
--- /dev/null
+++ b/bin/dbg/goopdateres_gu.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_hi.dll b/bin/dbg/goopdateres_hi.dll
new file mode 100644
index 0000000..4de0fee
--- /dev/null
+++ b/bin/dbg/goopdateres_hi.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_hr.dll b/bin/dbg/goopdateres_hr.dll
new file mode 100644
index 0000000..8e91577
--- /dev/null
+++ b/bin/dbg/goopdateres_hr.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_hu.dll b/bin/dbg/goopdateres_hu.dll
new file mode 100644
index 0000000..460bf96
--- /dev/null
+++ b/bin/dbg/goopdateres_hu.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_id.dll b/bin/dbg/goopdateres_id.dll
new file mode 100644
index 0000000..a726fae
--- /dev/null
+++ b/bin/dbg/goopdateres_id.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_is.dll b/bin/dbg/goopdateres_is.dll
new file mode 100644
index 0000000..04a3a01
--- /dev/null
+++ b/bin/dbg/goopdateres_is.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_it.dll b/bin/dbg/goopdateres_it.dll
new file mode 100644
index 0000000..8e7858e
--- /dev/null
+++ b/bin/dbg/goopdateres_it.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_iw.dll b/bin/dbg/goopdateres_iw.dll
new file mode 100644
index 0000000..40a964a
--- /dev/null
+++ b/bin/dbg/goopdateres_iw.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ja.dll b/bin/dbg/goopdateres_ja.dll
new file mode 100644
index 0000000..8422f56
--- /dev/null
+++ b/bin/dbg/goopdateres_ja.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_kn.dll b/bin/dbg/goopdateres_kn.dll
new file mode 100644
index 0000000..ce9f492
--- /dev/null
+++ b/bin/dbg/goopdateres_kn.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ko.dll b/bin/dbg/goopdateres_ko.dll
new file mode 100644
index 0000000..debd327
--- /dev/null
+++ b/bin/dbg/goopdateres_ko.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_lt.dll b/bin/dbg/goopdateres_lt.dll
new file mode 100644
index 0000000..5edc4c7
--- /dev/null
+++ b/bin/dbg/goopdateres_lt.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_lv.dll b/bin/dbg/goopdateres_lv.dll
new file mode 100644
index 0000000..3b797f5
--- /dev/null
+++ b/bin/dbg/goopdateres_lv.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ml.dll b/bin/dbg/goopdateres_ml.dll
new file mode 100644
index 0000000..ccae3af
--- /dev/null
+++ b/bin/dbg/goopdateres_ml.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_mr.dll b/bin/dbg/goopdateres_mr.dll
new file mode 100644
index 0000000..9c10c31
--- /dev/null
+++ b/bin/dbg/goopdateres_mr.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ms.dll b/bin/dbg/goopdateres_ms.dll
new file mode 100644
index 0000000..3bb8a37
--- /dev/null
+++ b/bin/dbg/goopdateres_ms.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_nl.dll b/bin/dbg/goopdateres_nl.dll
new file mode 100644
index 0000000..0bc145f
--- /dev/null
+++ b/bin/dbg/goopdateres_nl.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_no.dll b/bin/dbg/goopdateres_no.dll
new file mode 100644
index 0000000..e5b14f7
--- /dev/null
+++ b/bin/dbg/goopdateres_no.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_or.dll b/bin/dbg/goopdateres_or.dll
new file mode 100644
index 0000000..9384fd4
--- /dev/null
+++ b/bin/dbg/goopdateres_or.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_pl.dll b/bin/dbg/goopdateres_pl.dll
new file mode 100644
index 0000000..331c96f
--- /dev/null
+++ b/bin/dbg/goopdateres_pl.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_pt-BR.dll b/bin/dbg/goopdateres_pt-BR.dll
new file mode 100644
index 0000000..f670208
--- /dev/null
+++ b/bin/dbg/goopdateres_pt-BR.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_pt-PT.dll b/bin/dbg/goopdateres_pt-PT.dll
new file mode 100644
index 0000000..9c7e861
--- /dev/null
+++ b/bin/dbg/goopdateres_pt-PT.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ro.dll b/bin/dbg/goopdateres_ro.dll
new file mode 100644
index 0000000..413f4aa
--- /dev/null
+++ b/bin/dbg/goopdateres_ro.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ru.dll b/bin/dbg/goopdateres_ru.dll
new file mode 100644
index 0000000..86364ab
--- /dev/null
+++ b/bin/dbg/goopdateres_ru.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_sk.dll b/bin/dbg/goopdateres_sk.dll
new file mode 100644
index 0000000..7cd7da4
--- /dev/null
+++ b/bin/dbg/goopdateres_sk.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_sl.dll b/bin/dbg/goopdateres_sl.dll
new file mode 100644
index 0000000..77b02ff
--- /dev/null
+++ b/bin/dbg/goopdateres_sl.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_sr.dll b/bin/dbg/goopdateres_sr.dll
new file mode 100644
index 0000000..58e2bdf
--- /dev/null
+++ b/bin/dbg/goopdateres_sr.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_sv.dll b/bin/dbg/goopdateres_sv.dll
new file mode 100644
index 0000000..dae642b
--- /dev/null
+++ b/bin/dbg/goopdateres_sv.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ta.dll b/bin/dbg/goopdateres_ta.dll
new file mode 100644
index 0000000..ef365e8
--- /dev/null
+++ b/bin/dbg/goopdateres_ta.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_te.dll b/bin/dbg/goopdateres_te.dll
new file mode 100644
index 0000000..2a4d89a
--- /dev/null
+++ b/bin/dbg/goopdateres_te.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_th.dll b/bin/dbg/goopdateres_th.dll
new file mode 100644
index 0000000..b120690
--- /dev/null
+++ b/bin/dbg/goopdateres_th.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_tr.dll b/bin/dbg/goopdateres_tr.dll
new file mode 100644
index 0000000..b8d21a9
--- /dev/null
+++ b/bin/dbg/goopdateres_tr.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_uk.dll b/bin/dbg/goopdateres_uk.dll
new file mode 100644
index 0000000..8a77b32
--- /dev/null
+++ b/bin/dbg/goopdateres_uk.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_ur.dll b/bin/dbg/goopdateres_ur.dll
new file mode 100644
index 0000000..3eb0320
--- /dev/null
+++ b/bin/dbg/goopdateres_ur.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_vi.dll b/bin/dbg/goopdateres_vi.dll
new file mode 100644
index 0000000..c6f8102
--- /dev/null
+++ b/bin/dbg/goopdateres_vi.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_zh-CN.dll b/bin/dbg/goopdateres_zh-CN.dll
new file mode 100644
index 0000000..e8ddba5
--- /dev/null
+++ b/bin/dbg/goopdateres_zh-CN.dll
Binary files differ
diff --git a/bin/dbg/goopdateres_zh-TW.dll b/bin/dbg/goopdateres_zh-TW.dll
new file mode 100644
index 0000000..1c1eae4
--- /dev/null
+++ b/bin/dbg/goopdateres_zh-TW.dll
Binary files differ
diff --git a/bin/dbg/mi_exe_stub.exe b/bin/dbg/mi_exe_stub.exe
new file mode 100644
index 0000000..db76c58
--- /dev/null
+++ b/bin/dbg/mi_exe_stub.exe
Binary files differ
diff --git a/bin/dbg/npGoogleOneClick7.dll b/bin/dbg/npGoogleOneClick7.dll
new file mode 100644
index 0000000..ef476b7
--- /dev/null
+++ b/bin/dbg/npGoogleOneClick7.dll
Binary files differ
diff --git a/bin/opt/GoogleUpdate.exe b/bin/opt/GoogleUpdate.exe
new file mode 100644
index 0000000..3edfbc9
--- /dev/null
+++ b/bin/opt/GoogleUpdate.exe
Binary files differ
diff --git a/bin/opt/GoogleUpdateHelper.msi b/bin/opt/GoogleUpdateHelper.msi
new file mode 100644
index 0000000..7aab029
--- /dev/null
+++ b/bin/opt/GoogleUpdateHelper.msi
Binary files differ
diff --git a/bin/opt/GoopdateBho.dll b/bin/opt/GoopdateBho.dll
new file mode 100644
index 0000000..5e049db
--- /dev/null
+++ b/bin/opt/GoopdateBho.dll
Binary files differ
diff --git a/bin/opt/VERSION b/bin/opt/VERSION
new file mode 100644
index 0000000..dd7a11e
--- /dev/null
+++ b/bin/opt/VERSION
@@ -0,0 +1,10 @@
+# -*- Python -*-
+
+# 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 = 141  # 1-65535
+version_patch = 5    # 0-65535
+
+oneclick_plugin_version = '7'
diff --git a/bin/opt/goopdate.dll b/bin/opt/goopdate.dll
new file mode 100644
index 0000000..55ffed3
--- /dev/null
+++ b/bin/opt/goopdate.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ar.dll b/bin/opt/goopdateres_ar.dll
new file mode 100644
index 0000000..677cbef
--- /dev/null
+++ b/bin/opt/goopdateres_ar.dll
Binary files differ
diff --git a/bin/opt/goopdateres_bg.dll b/bin/opt/goopdateres_bg.dll
new file mode 100644
index 0000000..fbe6b62
--- /dev/null
+++ b/bin/opt/goopdateres_bg.dll
Binary files differ
diff --git a/bin/opt/goopdateres_bn.dll b/bin/opt/goopdateres_bn.dll
new file mode 100644
index 0000000..2af24fe
--- /dev/null
+++ b/bin/opt/goopdateres_bn.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ca.dll b/bin/opt/goopdateres_ca.dll
new file mode 100644
index 0000000..75140a8
--- /dev/null
+++ b/bin/opt/goopdateres_ca.dll
Binary files differ
diff --git a/bin/opt/goopdateres_cs.dll b/bin/opt/goopdateres_cs.dll
new file mode 100644
index 0000000..a711b84
--- /dev/null
+++ b/bin/opt/goopdateres_cs.dll
Binary files differ
diff --git a/bin/opt/goopdateres_da.dll b/bin/opt/goopdateres_da.dll
new file mode 100644
index 0000000..0a0db8c
--- /dev/null
+++ b/bin/opt/goopdateres_da.dll
Binary files differ
diff --git a/bin/opt/goopdateres_de.dll b/bin/opt/goopdateres_de.dll
new file mode 100644
index 0000000..84db35a
--- /dev/null
+++ b/bin/opt/goopdateres_de.dll
Binary files differ
diff --git a/bin/opt/goopdateres_el.dll b/bin/opt/goopdateres_el.dll
new file mode 100644
index 0000000..92b26ab
--- /dev/null
+++ b/bin/opt/goopdateres_el.dll
Binary files differ
diff --git a/bin/opt/goopdateres_en-GB.dll b/bin/opt/goopdateres_en-GB.dll
new file mode 100644
index 0000000..5eab2a8
--- /dev/null
+++ b/bin/opt/goopdateres_en-GB.dll
Binary files differ
diff --git a/bin/opt/goopdateres_en.dll b/bin/opt/goopdateres_en.dll
new file mode 100644
index 0000000..e411b07
--- /dev/null
+++ b/bin/opt/goopdateres_en.dll
Binary files differ
diff --git a/bin/opt/goopdateres_es-419.dll b/bin/opt/goopdateres_es-419.dll
new file mode 100644
index 0000000..3b82184
--- /dev/null
+++ b/bin/opt/goopdateres_es-419.dll
Binary files differ
diff --git a/bin/opt/goopdateres_es.dll b/bin/opt/goopdateres_es.dll
new file mode 100644
index 0000000..1b0115e
--- /dev/null
+++ b/bin/opt/goopdateres_es.dll
Binary files differ
diff --git a/bin/opt/goopdateres_et.dll b/bin/opt/goopdateres_et.dll
new file mode 100644
index 0000000..efd3dda
--- /dev/null
+++ b/bin/opt/goopdateres_et.dll
Binary files differ
diff --git a/bin/opt/goopdateres_fa.dll b/bin/opt/goopdateres_fa.dll
new file mode 100644
index 0000000..95fcc04
--- /dev/null
+++ b/bin/opt/goopdateres_fa.dll
Binary files differ
diff --git a/bin/opt/goopdateres_fi.dll b/bin/opt/goopdateres_fi.dll
new file mode 100644
index 0000000..77befb9
--- /dev/null
+++ b/bin/opt/goopdateres_fi.dll
Binary files differ
diff --git a/bin/opt/goopdateres_fil.dll b/bin/opt/goopdateres_fil.dll
new file mode 100644
index 0000000..2aedf97
--- /dev/null
+++ b/bin/opt/goopdateres_fil.dll
Binary files differ
diff --git a/bin/opt/goopdateres_fr.dll b/bin/opt/goopdateres_fr.dll
new file mode 100644
index 0000000..3adf3b6
--- /dev/null
+++ b/bin/opt/goopdateres_fr.dll
Binary files differ
diff --git a/bin/opt/goopdateres_gu.dll b/bin/opt/goopdateres_gu.dll
new file mode 100644
index 0000000..ec06166
--- /dev/null
+++ b/bin/opt/goopdateres_gu.dll
Binary files differ
diff --git a/bin/opt/goopdateres_hi.dll b/bin/opt/goopdateres_hi.dll
new file mode 100644
index 0000000..b193d95
--- /dev/null
+++ b/bin/opt/goopdateres_hi.dll
Binary files differ
diff --git a/bin/opt/goopdateres_hr.dll b/bin/opt/goopdateres_hr.dll
new file mode 100644
index 0000000..0d47b2f
--- /dev/null
+++ b/bin/opt/goopdateres_hr.dll
Binary files differ
diff --git a/bin/opt/goopdateres_hu.dll b/bin/opt/goopdateres_hu.dll
new file mode 100644
index 0000000..2629544
--- /dev/null
+++ b/bin/opt/goopdateres_hu.dll
Binary files differ
diff --git a/bin/opt/goopdateres_id.dll b/bin/opt/goopdateres_id.dll
new file mode 100644
index 0000000..f740719
--- /dev/null
+++ b/bin/opt/goopdateres_id.dll
Binary files differ
diff --git a/bin/opt/goopdateres_is.dll b/bin/opt/goopdateres_is.dll
new file mode 100644
index 0000000..a8fec23
--- /dev/null
+++ b/bin/opt/goopdateres_is.dll
Binary files differ
diff --git a/bin/opt/goopdateres_it.dll b/bin/opt/goopdateres_it.dll
new file mode 100644
index 0000000..168631b
--- /dev/null
+++ b/bin/opt/goopdateres_it.dll
Binary files differ
diff --git a/bin/opt/goopdateres_iw.dll b/bin/opt/goopdateres_iw.dll
new file mode 100644
index 0000000..6786e8e
--- /dev/null
+++ b/bin/opt/goopdateres_iw.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ja.dll b/bin/opt/goopdateres_ja.dll
new file mode 100644
index 0000000..9c30190
--- /dev/null
+++ b/bin/opt/goopdateres_ja.dll
Binary files differ
diff --git a/bin/opt/goopdateres_kn.dll b/bin/opt/goopdateres_kn.dll
new file mode 100644
index 0000000..a885236
--- /dev/null
+++ b/bin/opt/goopdateres_kn.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ko.dll b/bin/opt/goopdateres_ko.dll
new file mode 100644
index 0000000..af6b81e
--- /dev/null
+++ b/bin/opt/goopdateres_ko.dll
Binary files differ
diff --git a/bin/opt/goopdateres_lt.dll b/bin/opt/goopdateres_lt.dll
new file mode 100644
index 0000000..e295ac4
--- /dev/null
+++ b/bin/opt/goopdateres_lt.dll
Binary files differ
diff --git a/bin/opt/goopdateres_lv.dll b/bin/opt/goopdateres_lv.dll
new file mode 100644
index 0000000..21ceddd
--- /dev/null
+++ b/bin/opt/goopdateres_lv.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ml.dll b/bin/opt/goopdateres_ml.dll
new file mode 100644
index 0000000..1d97cfc
--- /dev/null
+++ b/bin/opt/goopdateres_ml.dll
Binary files differ
diff --git a/bin/opt/goopdateres_mr.dll b/bin/opt/goopdateres_mr.dll
new file mode 100644
index 0000000..f95d50a
--- /dev/null
+++ b/bin/opt/goopdateres_mr.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ms.dll b/bin/opt/goopdateres_ms.dll
new file mode 100644
index 0000000..c32c882
--- /dev/null
+++ b/bin/opt/goopdateres_ms.dll
Binary files differ
diff --git a/bin/opt/goopdateres_nl.dll b/bin/opt/goopdateres_nl.dll
new file mode 100644
index 0000000..b105d58
--- /dev/null
+++ b/bin/opt/goopdateres_nl.dll
Binary files differ
diff --git a/bin/opt/goopdateres_no.dll b/bin/opt/goopdateres_no.dll
new file mode 100644
index 0000000..1d6e678
--- /dev/null
+++ b/bin/opt/goopdateres_no.dll
Binary files differ
diff --git a/bin/opt/goopdateres_or.dll b/bin/opt/goopdateres_or.dll
new file mode 100644
index 0000000..a345248
--- /dev/null
+++ b/bin/opt/goopdateres_or.dll
Binary files differ
diff --git a/bin/opt/goopdateres_pl.dll b/bin/opt/goopdateres_pl.dll
new file mode 100644
index 0000000..7418f67
--- /dev/null
+++ b/bin/opt/goopdateres_pl.dll
Binary files differ
diff --git a/bin/opt/goopdateres_pt-BR.dll b/bin/opt/goopdateres_pt-BR.dll
new file mode 100644
index 0000000..c9ae3a8
--- /dev/null
+++ b/bin/opt/goopdateres_pt-BR.dll
Binary files differ
diff --git a/bin/opt/goopdateres_pt-PT.dll b/bin/opt/goopdateres_pt-PT.dll
new file mode 100644
index 0000000..9cec7a6
--- /dev/null
+++ b/bin/opt/goopdateres_pt-PT.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ro.dll b/bin/opt/goopdateres_ro.dll
new file mode 100644
index 0000000..594eead
--- /dev/null
+++ b/bin/opt/goopdateres_ro.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ru.dll b/bin/opt/goopdateres_ru.dll
new file mode 100644
index 0000000..f03e864
--- /dev/null
+++ b/bin/opt/goopdateres_ru.dll
Binary files differ
diff --git a/bin/opt/goopdateres_sk.dll b/bin/opt/goopdateres_sk.dll
new file mode 100644
index 0000000..14d712f
--- /dev/null
+++ b/bin/opt/goopdateres_sk.dll
Binary files differ
diff --git a/bin/opt/goopdateres_sl.dll b/bin/opt/goopdateres_sl.dll
new file mode 100644
index 0000000..43cca71
--- /dev/null
+++ b/bin/opt/goopdateres_sl.dll
Binary files differ
diff --git a/bin/opt/goopdateres_sr.dll b/bin/opt/goopdateres_sr.dll
new file mode 100644
index 0000000..40d1905
--- /dev/null
+++ b/bin/opt/goopdateres_sr.dll
Binary files differ
diff --git a/bin/opt/goopdateres_sv.dll b/bin/opt/goopdateres_sv.dll
new file mode 100644
index 0000000..7861043
--- /dev/null
+++ b/bin/opt/goopdateres_sv.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ta.dll b/bin/opt/goopdateres_ta.dll
new file mode 100644
index 0000000..827046d
--- /dev/null
+++ b/bin/opt/goopdateres_ta.dll
Binary files differ
diff --git a/bin/opt/goopdateres_te.dll b/bin/opt/goopdateres_te.dll
new file mode 100644
index 0000000..255720b
--- /dev/null
+++ b/bin/opt/goopdateres_te.dll
Binary files differ
diff --git a/bin/opt/goopdateres_th.dll b/bin/opt/goopdateres_th.dll
new file mode 100644
index 0000000..0a2c1bf
--- /dev/null
+++ b/bin/opt/goopdateres_th.dll
Binary files differ
diff --git a/bin/opt/goopdateres_tr.dll b/bin/opt/goopdateres_tr.dll
new file mode 100644
index 0000000..d9268ad
--- /dev/null
+++ b/bin/opt/goopdateres_tr.dll
Binary files differ
diff --git a/bin/opt/goopdateres_uk.dll b/bin/opt/goopdateres_uk.dll
new file mode 100644
index 0000000..2157f5d
--- /dev/null
+++ b/bin/opt/goopdateres_uk.dll
Binary files differ
diff --git a/bin/opt/goopdateres_ur.dll b/bin/opt/goopdateres_ur.dll
new file mode 100644
index 0000000..fd0802d
--- /dev/null
+++ b/bin/opt/goopdateres_ur.dll
Binary files differ
diff --git a/bin/opt/goopdateres_vi.dll b/bin/opt/goopdateres_vi.dll
new file mode 100644
index 0000000..21043a0
--- /dev/null
+++ b/bin/opt/goopdateres_vi.dll
Binary files differ
diff --git a/bin/opt/goopdateres_zh-CN.dll b/bin/opt/goopdateres_zh-CN.dll
new file mode 100644
index 0000000..503e81c
--- /dev/null
+++ b/bin/opt/goopdateres_zh-CN.dll
Binary files differ
diff --git a/bin/opt/goopdateres_zh-TW.dll b/bin/opt/goopdateres_zh-TW.dll
new file mode 100644
index 0000000..2c6bdba
--- /dev/null
+++ b/bin/opt/goopdateres_zh-TW.dll
Binary files differ
diff --git a/bin/opt/mi_exe_stub.exe b/bin/opt/mi_exe_stub.exe
new file mode 100644
index 0000000..830e3e6
--- /dev/null
+++ b/bin/opt/mi_exe_stub.exe
Binary files differ
diff --git a/bin/opt/npGoogleOneClick7.dll b/bin/opt/npGoogleOneClick7.dll
new file mode 100644
index 0000000..66e4829
--- /dev/null
+++ b/bin/opt/npGoogleOneClick7.dll
Binary files differ
diff --git a/build.scons b/build.scons
new file mode 100644
index 0000000..b182e7a
--- /dev/null
+++ b/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')
+
+# Copy useful test files to the build output.
+files = [
+    '$MAIN_DIR/VERSION',
+    '$MAIN_DIR/data/GoogleUpdate.ini',
+    '$MAIN_DIR/data/make_dev_system.reg',
+    '$MAIN_DIR/data/make_qa_system.reg',
+    ]
+env.Replicate('$STAGING_DIR', files)
+
+
diff --git a/clickonce/add_trusturlparams.py b/clickonce/add_trusturlparams.py
new file mode 100644
index 0000000..18a7357
--- /dev/null
+++ b/clickonce/add_trusturlparams.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python2.4
+#
+# 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.
+# ========================================================================
+
+"""
+mage.exe does not provide a way to add the trustURLParameters attribute to an
+application manifest. This script fills that gap. It also adds in the
+localized display name, to get around issues with the Python commands
+module.
+"""
+
+import sys
+import os
+import getopt
+import commands
+
+
+def _AddTrustURLParametersAndName(manifest_file, output_file, display_name):
+  f_in = open(manifest_file, 'r')
+  manifest_contents = f_in.read()
+  f_in.close()
+
+  manifest_contents = manifest_contents.replace('<deployment ', \
+      '<deployment trustURLParameters="true" ')
+  manifest_contents = manifest_contents.replace('\"xxxXXXxxx', \
+      '\"%s' % display_name)
+
+  f_out = open(output_file, 'w')
+  # Works without needing to write the codecs.BOM_UTF8 at the beginning of the
+  # file. May need to write this at some point though.
+  f_out.write(manifest_contents)
+  f_out.close()
+
+
+def _Usage():
+  """Prints out script usage information."""
+  print """
+add_trusturlparams.py: Modify the given manifest file by adding in a
+trustURLParameters=true to the deployment section. Also substitutes
+the dummy name xxxXXXxxx with the localized display name.
+
+Usage:
+  add_trusturlparams.py [--help
+                         | --manifest_file filename
+                         | --output_file filename
+                           --display_name {i18n display name}]
+
+Options:
+  --help                    Show this information.
+  --manifest_file filename     Path/name of input/output manifest file.
+  --output_file filename       Path/name of an optional output manifest file.
+  --display_name  name         i18n display name.
+"""
+
+
+def _Main():
+  # use getopt to parse the option and argument list; this may raise, but
+  # don't catch it
+  _ARGUMENT_LIST = ["help", "manifest_file=", "output_file=", "display_name="]
+  (opts, args) = getopt.getopt(sys.argv[1:], "", _ARGUMENT_LIST)
+  if not opts or ("--help", "") in opts:
+    _Usage()
+    sys.exit()
+
+  manifest_file = ""
+  output_file = ""
+  display_name = ""
+
+  for (o, v) in opts:
+    if o == "--manifest_file":
+      manifest_file = v
+    if o == "--output_file":
+      output_file = v
+    if o == "--display_name":
+      display_name = v
+
+  # make sure we have work to do
+  if not manifest_file:
+    raise SystemExit("no manifest_filename specified")
+  if not display_name:
+    raise SystemExit("no display_name specified")
+
+  # overwrite existing file if no separate output specified
+  if not output_file:
+    output_file = manifest_file
+
+  _AddTrustURLParametersAndName(manifest_file, output_file, display_name)
+  sys.exit()
+
+
+if __name__ == "__main__":
+  _Main()
+
diff --git a/clickonce/build.scons b/clickonce/build.scons
new file mode 100644
index 0000000..d28f46a
--- /dev/null
+++ b/clickonce/build.scons
@@ -0,0 +1,101 @@
+#!/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.
+# ========================================================================
+
+# Note: The localized ClickOnce deployment manifest is generated in
+# installers/build.scons.
+
+
+Import('env')
+
+clickonce_name = 'clickonce_bootstrap'
+clickonce_binary = clickonce_name + '.exe'
+clickonce_res = clickonce_name + '.res'
+
+
+#
+# Build the .res file.
+#
+env.RES(target=clickonce_res, source='clickonce_bootstrap.rc')
+
+
+#
+# Generate the executable.
+#
+exe_action = 'csc.exe /target:winexe /platform:x86 /out:$TARGET /win32res:%s '\
+    '$SOURCES' % (env.File(clickonce_res).path)
+
+exe_output = env.Command(
+    target=clickonce_binary,
+    source='clickonce_bootstrap.cs',
+    action=exe_action
+)
+
+# Inform Hammer that the .res file must be built before the executeable
+env.Requires(exe_output, clickonce_res)
+
+clickonce_deploy_dir = '$TARGET_ROOT/Clickonce_Deployment'
+clickonce_deploy_bin_dir = clickonce_deploy_dir + '/bin'
+
+# Copy executable into Clickonce deployment directory.
+replicate_output = env.Replicate(clickonce_deploy_bin_dir, exe_output)
+
+
+#
+# Generate the application manifest.
+#
+v = env['product_version'][0]
+version_string = '%d.%d.%d.%d' % (v[0], v[1], v[2], v[3])
+
+generate_manifest_action = ('@mage -New Application -ToFile $TARGET -Name %s'
+    ' -Version %s -FromDirectory %s -Processor x86 -TrustLevel FullTrust' % (
+    clickonce_name, version_string, env.Dir(clickonce_deploy_bin_dir).abspath))
+
+unsigned_manifest = env.Command(
+    target=clickonce_binary + '.manifest',
+    source=replicate_output,
+    action=generate_manifest_action
+)
+
+# Sign the application manifest.
+sign_manifest_cmd =('@mage -Sign $SOURCE -ToFile $TARGET -TimestampUri '
+                    'http://timestamp.verisign.com/scripts/timstamp.dll ')
+
+if env.Bit('build_server'):
+  sign_manifest_cmd += '-CertHash fe5008fe0da7a2033816752d6eafe95214f5a7e1'
+else:
+  sign_manifest_cmd += '-CertFile %s -Password %s' % (
+      GetOption('authenticode_file'), GetOption('authenticode_password'))
+
+signed_manifest = env.Command(
+    target='%s/%s.manifest' % (clickonce_deploy_dir, clickonce_binary),
+    source=unsigned_manifest,
+    action=sign_manifest_cmd
+)
+
+
+# Instruct Hammer to regenerate the manifests when either of these
+# executables change
+env.Depends(
+    target = [
+        unsigned_manifest,
+        signed_manifest,
+        ],
+    dependency = [
+        '%s/%s' % (clickonce_deploy_bin_dir, clickonce_binary),
+        '%s/GoogleUpdateSetup.exe' % (clickonce_deploy_bin_dir),
+        ]
+)
diff --git a/clickonce/clickonce_bootstrap.cs b/clickonce/clickonce_bootstrap.cs
new file mode 100644
index 0000000..4a48eb1
--- /dev/null
+++ b/clickonce/clickonce_bootstrap.cs
@@ -0,0 +1,70 @@
+// 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.
+// ========================================================================
+
+// C# stub that allows a ClickOnce install of an Omaha application.
+
+using System;
+using System.Deployment.Application;
+using System.Diagnostics;
+using System.IO;
+using System.Security;
+using System.Security.Permissions;
+using System.Windows.Forms;
+using System.Web;
+
+namespace ClickOnceBootstrap {
+  static class ClickOnceEntry {
+    [STAThread]
+    static void Main() {
+      try {
+        // Try to get FullTrust. Will throw if we cannot get it.
+        new PermissionSet(PermissionState.Unrestricted).Demand();
+
+        if (!ApplicationDeployment.IsNetworkDeployed) {
+          // Only support running via ClickOnce.
+          return;
+        }
+
+        string query_string =
+            ApplicationDeployment.CurrentDeployment.ActivationUri.Query;
+        if (query_string.Length < 2) {
+          // Query string will be of the form "?xyz=abc". Should have atleast
+          // a question mark and atleast a single character to qualify as a
+          // valid query string. Hence the check against "2".
+          return;
+        }
+        // Remove the '?' prefix.
+        query_string = query_string.Substring(1);
+        query_string = HttpUtility.UrlDecode(query_string);
+        string setup_path = Path.Combine(Application.StartupPath,
+                                          "GoogleUpdateSetup.exe");
+
+        ProcessStartInfo psi = new ProcessStartInfo();
+        psi.FileName = setup_path;
+        psi.Verb = "open";
+        psi.Arguments = "/installsource clickonce /install \"";
+        psi.Arguments += query_string;
+        psi.Arguments += "\"";
+        Process.Start(psi);
+      } catch(Exception e) {
+        MessageBox.Show(e.ToString());
+        return;
+      }
+
+      return;
+    }
+  }
+}
+
diff --git a/clickonce/clickonce_bootstrap.manifest b/clickonce/clickonce_bootstrap.manifest
new file mode 100644
index 0000000..c3263b5
--- /dev/null
+++ b/clickonce/clickonce_bootstrap.manifest
@@ -0,0 +1,10 @@
+<?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/clickonce/clickonce_bootstrap.rc b/clickonce/clickonce_bootstrap.rc
new file mode 100644
index 0000000..f291ca9
--- /dev/null
+++ b/clickonce/clickonce_bootstrap.rc
@@ -0,0 +1,17 @@
+// 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.
+// ========================================================================
+
+1 24 "clickonce_bootstrap.manifest"
+
diff --git a/common/ATLRegMapEx.h b/common/ATLRegMapEx.h
new file mode 100644
index 0000000..2a71575
--- /dev/null
+++ b/common/ATLRegMapEx.h
@@ -0,0 +1,230 @@
+// 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
new file mode 100644
index 0000000..95a1727
--- /dev/null
+++ b/common/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/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/accounts.h b/common/accounts.h
new file mode 100644
index 0000000..26c713d
--- /dev/null
+++ b/common/accounts.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef OMAHA_COMMON_ACCOUNTS_H__
+#define OMAHA_COMMON_ACCOUNTS_H__
+
+#include <windows.h>
+#include <atlcoll.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+namespace accounts {
+
+// Populates sid_array with string SIDs for all users, that have profiles
+// on PC. Includes only user SIDs, no groups, computers or aliases.
+HRESULT GetAllUserSids(CSimpleArray<CString>* sid_array);
+
+// Looks up account info for given SID.
+// sid - SID to look up account info for.
+// On success populates:
+// name - name on the account
+// domain - domain name for account
+// user_type - the type of the passed SID, possible values:
+//   SidTypeUser
+//   SidTypeGroup
+//   SidTypeDomain
+//   SidTypeAlias
+//   SidTypeWellKnownGroup
+//   SidTypeDeletedAccount
+//   SidTypeInvalid
+//   SidTypeUnknown
+//   SidTypeComputer
+HRESULT GetUserInfo(const wchar_t* sid,
+                    CString* name,
+                    CString* domain,
+                    SID_NAME_USE* user_type);
+
+}  // namespace accounts
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_ACCOUNTS_H__
diff --git a/common/app_util.cc b/common/app_util.cc
new file mode 100644
index 0000000..8d1d540
--- /dev/null
+++ b/common/app_util.cc
@@ -0,0 +1,242 @@
+// 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
new file mode 100644
index 0000000..8ff9f55
--- /dev/null
+++ b/common/app_util.h
@@ -0,0 +1,133 @@
+// 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
new file mode 100644
index 0000000..fc13f89
--- /dev/null
+++ b/common/app_util_unittest.cc
@@ -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.
+// ========================================================================
+
+#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
new file mode 100644
index 0000000..48d13e2
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..b0c6eb1
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..910939b
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..675dde8
--- /dev/null
+++ b/common/atl_regexp.h
@@ -0,0 +1,145 @@
+// 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
new file mode 100644
index 0000000..6ca3cec
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..069c0b7
--- /dev/null
+++ b/common/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_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
new file mode 100644
index 0000000..2e1deb9
--- /dev/null
+++ b/common/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/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/atlconvfix.h b/common/atlconvfix.h
new file mode 100644
index 0000000..76ffaa4
--- /dev/null
+++ b/common/atlconvfix.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.
+// ========================================================================
+//
+// atlconvfix.h
+//
+// This file is included in the precompile headers.
+// Do not include the base/basictypes.h here.
+
+#ifndef OMAHA_COMMON_ATLCONVFIX_H_
+#define OMAHA_COMMON_ATLCONVFIX_H_
+
+#ifndef DISALLOW_EVIL_CONSTRUCTORS
+// 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&);                    \
+  void operator=(const TypeName&)
+#endif
+
+// These use alloca which can be dangerous,
+// so we don't allow them to be used.  Use
+// CA2[C]W, etc. instead.
+#undef A2W
+#undef W2A
+#undef A2W_EX
+#undef W2A_EX
+#undef USES_CONVERSION
+
+#ifdef DEBUG
+// The DestroyBuffer template and the macros following it are
+// all there to ensure that when the string classes get destroyed,
+// so does the string that they return since it is no longer valid.
+// Without them it is very easy to make simple and hard to catch mistakes
+// when using the atl C*2[C]* string converstion classes.
+
+// In non-debug code, the atl string macros do not need to be destroyed,
+// so they aren't wrapped there.
+
+template <int buffer_length, template <int buffer_length> class ConversionClass,
+          typename StringType>
+class DestroyBuffer : public ConversionClass<buffer_length> {
+ public:
+  DestroyBuffer(StringType string) : ConversionClass<buffer_length>(string) {
+  }
+
+  DestroyBuffer(StringType string, UINT code_page) :
+      ConversionClass<buffer_length>(string, code_page) {
+  }
+
+  ~DestroyBuffer() {
+    memset(m_szBuffer, 0xdd, sizeof(m_szBuffer));
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(DestroyBuffer);
+};
+
+#define DECLARED_DESTROY_NAME(base_type) DestroyBuffer##base_type
+#define DECLARE_DESTROY_TYPES(base_type, string_type) \
+  template <int buffer_length = 128> \
+  class DECLARED_DESTROY_NAME(base_type##EX) : \
+      public DestroyBuffer<buffer_length, base_type##EX, string_type> { \
+   public: \
+    DECLARED_DESTROY_NAME(base_type##EX)(string_type string) : \
+      DestroyBuffer<buffer_length, base_type##EX, string_type>(string) {  \
+    } \
+    DECLARED_DESTROY_NAME(base_type##EX)(string_type string, \
+                                         UINT code_page) : \
+      DestroyBuffer<buffer_length, base_type##EX, string_type>(string, \
+                                                               code_page) { \
+    } \
+   private: \
+    DISALLOW_EVIL_CONSTRUCTORS(DECLARED_DESTROY_NAME(base_type##EX)); \
+  }; \
+  typedef DECLARED_DESTROY_NAME(base_type##EX)<> \
+      DECLARED_DESTROY_NAME(base_type)
+
+DECLARE_DESTROY_TYPES(CW2W, LPCWSTR);
+DECLARE_DESTROY_TYPES(CW2A, LPCWSTR);
+DECLARE_DESTROY_TYPES(CA2W, LPCSTR);
+DECLARE_DESTROY_TYPES(CA2A, LPCSTR);
+
+#define CW2WEX DECLARED_DESTROY_NAME(CW2WEX)
+#define CW2AEX DECLARED_DESTROY_NAME(CW2AEX)
+#define CA2WEX DECLARED_DESTROY_NAME(CA2WEX)
+#define CA2AEX DECLARED_DESTROY_NAME(CA2AEX)
+#define CW2W   DECLARED_DESTROY_NAME(CW2W)
+#define CW2A   DECLARED_DESTROY_NAME(CW2A)
+#define CA2W   DECLARED_DESTROY_NAME(CA2W)
+#define CA2A   DECLARED_DESTROY_NAME(CA2A)
+#endif // DEBUG
+
+#endif // OMAHA_COMMON_ATLCONVFIX_H_
diff --git a/common/auto_any.h b/common/auto_any.h
new file mode 100644
index 0000000..4b550a2
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..3e64ae9
--- /dev/null
+++ b/common/browser_utils.cc
@@ -0,0 +1,580 @@
+// 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/issue?id=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 {
+    *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_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 FF reg key contains a -url %1 value, since we only want to return
+        // the path, we remove this.
+        ReplaceCString(*path, _T("-url \"%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
new file mode 100644
index 0000000..068456e
--- /dev/null
+++ b/common/browser_utils.h
@@ -0,0 +1,89 @@
+// 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,
+  BROWSER_IE,
+  BROWSER_FIREFOX,
+  // 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
new file mode 100644
index 0000000..82095b3
--- /dev/null
+++ b/common/browser_utils_unittest.cc
@@ -0,0 +1,209 @@
+// 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");
+
+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(BrowserUtilsTest, GetBrowserImagePath) {
+  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;
+  }
+
+  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());
+  }
+
+  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());
+  }
+
+  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));
+}
+
+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
new file mode 100644
index 0000000..5bee199
--- /dev/null
+++ b/common/build.scons
@@ -0,0 +1,99 @@
+# 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')
+
+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'])
+
+# Build these into a library.
+local_env.ComponentLibrary('common', inputs)
diff --git a/common/cgi.cc b/common/cgi.cc
new file mode 100644
index 0000000..28aa4d1
--- /dev/null
+++ b/common/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/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.h b/common/cgi.h
new file mode 100644
index 0000000..bb78b08
--- /dev/null
+++ b/common/cgi.h
@@ -0,0 +1,51 @@
+// 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_CGI_H__
+#define OMAHA_COMMON_CGI_H__
+
+#include <tchar.h>
+
+namespace omaha {
+
+class CGI {
+ public:
+  // Maximum factor by which EscapeString() can increase the string length
+  static const int kEscapeFactor = 3;
+
+  // EscapeString() converts funky characters found in "src[0,srcn-1]" into
+  // escape sequences.  The escaped string is placed back in "dst".  At most
+  // "dstn" characters are written into "dst", including the
+  // null-termination byte.  Returns true if
+  // successful, false if the escaped string will not fit entirely in "dstn"
+  // characters.  Since escaping can increase the length, "dst" should not
+  // be the same as "src".
+
+  // These functions always return a null-terminated string, even if they
+  // fail (e.g. if a bad escape sequence is found).  As a consequence, you
+  // must pass in a dstn > 0.
+  // If you want to guarantee that the result will fit, you need
+  //      dstn >= kEscapeFactor * srcn + 1
+  // for EscapeString and
+  //      dstn >= srcn + 1
+  // for UnescapeString.
+
+  static bool EscapeString(const TCHAR* src, int srcn, TCHAR* dst, int dstn);
+  static bool UnescapeString(const TCHAR* src, int srcn, TCHAR* dst, int dstn);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CGI_H__
diff --git a/common/cgi_unittest.cc b/common/cgi_unittest.cc
new file mode 100644
index 0000000..a4f1394
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..1302d9a
--- /dev/null
+++ b/common/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/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/clipboard.h b/common/clipboard.h
new file mode 100644
index 0000000..832e853
--- /dev/null
+++ b/common/clipboard.h
@@ -0,0 +1,29 @@
+// 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_CLIPBOARD_H__
+#define OMAHA_COMMON_CLIPBOARD_H__
+
+#include <windows.h>
+#include <tchar.h>
+
+namespace omaha {
+
+// Copies the given string to the system clipboard.
+void SetClipboard(const TCHAR *string_to_set);
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CLIPBOARD_H__
diff --git a/common/commands.cc b/common/commands.cc
new file mode 100644
index 0000000..946ed91
--- /dev/null
+++ b/common/commands.cc
@@ -0,0 +1,565 @@
+// 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);
+
+  *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);
+
+  *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));
+  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;
+    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) {
+    // 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/common/commands.h
new file mode 100644
index 0000000..c2a857b
--- /dev/null
+++ b/common/commands.h
@@ -0,0 +1,159 @@
+// 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
+//
+// Class CommandParsing supports two kinds of command line options:
+// 1) Traditional option, like "-b -v 100"
+// 2) Name-value-pairs, like "b=&v=100"
+
+#ifndef OMAHA_COMMON_COMMANDS_H_
+#define OMAHA_COMMON_COMMANDS_H_
+
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+enum ThreeValue {
+  VALUE_NOT_SET = 0,
+  TRUE_VALUE = 1,
+  FALSE_VALUE = 2
+};
+
+enum CommandOptionType {
+  COMMAND_OPTION_BOOL = 0x1,
+  COMMAND_OPTION_INT = 0x2,
+  COMMAND_OPTION_UINT = 0x3,
+  COMMAND_OPTION_STRING = 0x4,
+  COMMAND_OPTION_THREE = 0x5,
+  COMMAND_OPTION_UNESCAPE = 0x1000,
+  COMMAND_OPTION_MULTIPLE = 0x2000
+};
+
+#define COMMAND_OPTION_FLAGS_MASK   0x0FFF
+
+struct CommandOption {
+  void Init(const TCHAR* name, CommandOptionType type,
+            void* value, int max_value_len);
+  void Copy(const CommandOption& option);
+
+  CString name;
+  CommandOptionType type;
+  void* value;
+  int max_value_len;
+};
+
+class CommandParsingSimple {
+ public:
+  // Static Helper function that splits a command line
+  // string into executable and any arguments
+  static HRESULT SplitExeAndArgs(const TCHAR* cmd_line,
+                                 CString* exe,
+                                 CString* args);
+
+  // Static Helper function that splits a command line
+  // string into executable and any arguments. Tries to
+  // guess the positioning of the EXE argument in cases
+  // where the EXE argument has spaces and is not enclosed
+  // in quotes. For instance, earlier versions of Google Desktop
+  // used to have an "Uninstall" string of the form:
+  // C:\Program Files\Google\Google Toolbar\GoogleToolbarSetup.exe -uninstall
+  // This function is meant to accomodate such cases.
+  static HRESULT SplitExeAndArgsGuess(const TCHAR* cmd_line,
+                                      CString* exe,
+                                      CString* args);
+
+  // Static Helper function that returns the number of arguments
+  // in the passed in cmd_line
+  static HRESULT GetNumberOfArgs(const TCHAR* cmd_line, uint32* number_of_args);
+
+  // Converted to a string
+  HRESULT ToString(CString* cmd_line);
+
+ protected:
+  // Constructor
+  CommandParsingSimple();
+
+  // Constructor
+  explicit CommandParsingSimple(TCHAR separator);
+
+  // Parse a command line string into args
+  HRESULT ParseSimple(const TCHAR* cmd_line);
+
+  // Get the arg at specified position from the command line
+  HRESULT GetAt(uint32 position, CString* arg);
+
+  // Remove the arg at specified position from the command line
+  HRESULT RemoveAt(uint32 position);
+
+  TCHAR separator_;                   // Separator
+  std::vector<CString> args_;         // Splitted args
+
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(CommandParsingSimple);
+};
+
+
+class CommandParsing : public CommandParsingSimple {
+ public:
+  // Constructor
+  CommandParsing(CommandOption* options, int options_count);
+
+  CommandParsing(CommandOption* options, int options_count,
+                 TCHAR separator, bool as_name_value_pair);
+
+  // Parse a command line string
+  HRESULT Parse(const TCHAR* cmd_line, bool ignore_unknown_args);
+
+  // Parse a list of command line arguments
+  HRESULT ParseArguments(int argc, TCHAR* argv[]);
+
+  // Remove an option from the command line
+  HRESULT Remove(const TCHAR* option_name);
+
+ private:
+  // Internal parsing
+  HRESULT InternalParse(bool ignore_unknown_args);
+
+  // Extract the name
+  HRESULT ExtractName(CString* name, std::vector<CString>::const_iterator* it);
+
+  // Extract the value
+  // Also validate the value length if necessary
+  HRESULT ExtractValue(const CommandOption& option,
+                       CString* value,
+                       std::vector<CString>::const_iterator* it,
+                       const std::vector<CString>::const_iterator& end);
+
+  // Set the parsed value
+  template<class T>
+  static void SetParsedValue(const CommandOption& option, const T& value);
+
+  // Helper function to find an option in the CommandOption list
+  int FindOption(const TCHAR* option_name);
+
+  CommandOption* options_;            // Command-line option list
+  int options_count_;                 // Count of command-line options
+  bool as_name_value_pair_;           // Parse as name-value-pair
+
+  DISALLOW_EVIL_CONSTRUCTORS(CommandParsing);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_COMMANDS_H_
diff --git a/common/commands_unittest.cc b/common/commands_unittest.cc
new file mode 100644
index 0000000..66a538f
--- /dev/null
+++ b/common/commands_unittest.cc
@@ -0,0 +1,281 @@
+// 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/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 kIEBrowserQuotedExe \
+  _T("\"C:\\PROGRAM FILES\\Internet Explorer\\iexplore.exe\"")
+
+#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(exe, kIEBrowserQuotedExeResult);
+  EXPECT_STREQ(args, kIEBrowserQuotedArgsResult);
+}
+
+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(exe, kIEBrowserQuotedExeResult);
+  EXPECT_STREQ(args, kIEBrowserQuotedArgsResult);
+
+  // Test to make sure SplitExeAndArgsGuess correctly splits
+  // an improperly constructed "Uninstall" command line
+  ASSERT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserUnquotedCommandLine,
+                                               &exe,
+                                               &args));
+
+  EXPECT_STREQ(exe, kIEBrowserUnquotedExe);
+  EXPECT_STREQ(args, kIEBrowserUnquotedArgs);
+
+  // 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(exe, kGEUninstallExe);
+  EXPECT_STREQ(args, kGEUninstallArgs);
+}
+
+}  // namespace omaha
+
diff --git a/common/commontypes.h b/common/commontypes.h
new file mode 100644
index 0000000..bcc7bfe
--- /dev/null
+++ b/common/commontypes.h
@@ -0,0 +1,66 @@
+// 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/const_addresses.h b/common/const_addresses.h
new file mode 100644
index 0000000..c3cf3ec
--- /dev/null
+++ b/common/const_addresses.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.
+// ========================================================================
+//
+// 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");
+
+// WebPlugin checks
+const TCHAR* const kUrlWebPluginCheck =
+    _T("https://tools.google.com/service/update2/oneclick");
+
+// 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://tools.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
new file mode 100644
index 0000000..2d50181
--- /dev/null
+++ b/common/const_cmd_line.h
@@ -0,0 +1,285 @@
+// 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.
+// ========================================================================
+//
+// Constants used as command line arguments.
+
+#ifndef OMAHA_COMMON_CONST_CMD_LINE_H__
+#define OMAHA_COMMON_CONST_CMD_LINE_H__
+
+namespace omaha {
+
+//
+// Externally initiated modes.
+// These modes are invoked by or on metainstallers or by the OneClick plugin  .
+//
+
+// The "install" switch indicates installing Google Update 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.
+const TCHAR* const kCmdLineUpdate = _T("update");
+
+// The "recover" switch indicates Google Update is to be repaired due to a
+// Code Red scenario.
+const TCHAR* const kCmdLineRecover = _T("recover");
+
+// The "pi" switch indicates that this came from a webplugin.
+// Requires two subarguments "siteurl" and "{args}" where
+// siteurl is the base URL where the plugin ran from and {args}
+// are the args to pass on once validation is complete.
+const TCHAR* const kCmdLineWebPlugin = _T("pi");
+
+//
+// Main operating modes
+//
+
+// The "c" option indicates a core process.
+const TCHAR* const kCmdLineCore = _T("c");
+
+// Specifies to not kick off the crash handler from the Core.
+const TCHAR* const kCmdLineNoCrashHandler = _T("nocrashserver");
+
+// The "crashhandler" option indicates a crash handler process.
+const TCHAR* const kCmdLineCrashHandler = _T("crashhandler");
+
+// Types of "Workers"
+
+// The "handoff" switch indicates a worker to perform an interactive install of
+// 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.
+// ig = Install Google Update.
+const TCHAR* const kCmdLineFinishGoogleUpdateInstall = _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.
+const TCHAR* const kCmdLineRegisterProduct = _T("registerproduct");
+
+// The "unregisterproduct" switch will unregister a product GUID from Clients.
+const TCHAR* const kCmdLineUnregisterProduct = _T("unregisterproduct");
+
+//
+// Minor modes
+//
+
+// The "svc" switch indicates that goopdate runs as a service.
+const TCHAR* const kCmdLineService = _T("svc");
+
+// The "regsvc" switch is used to register the service. Only used by unit
+// tests at the moment.
+const TCHAR* const kCmdLineRegisterService = _T("regsvc");
+
+// The "unregsvc" switch is used to unregister the service. Only used by
+// unit tests at the moment.
+const TCHAR* const kCmdLineUnregisterService = _T("unregsvc");
+
+// The "/comsvc" switch indicates that has the service is being invoked via COM.
+// This switch will be passed in via ServiceParameters.
+const TCHAR* const kCmdLineServiceComServer = _T("/comsvc");
+
+// The "regserver" switch indicates that goopdate 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
+// COM registration.
+const TCHAR* const kCmdUnregServer = _T("unregserver");
+
+// The "report" switch makes Omaha upload the crash report.
+const TCHAR* const kCmdLineReport = _T("report");
+
+// The "custom_info_filename" switch specifies the file that contains custom
+// crash info.
+const TCHAR* const kCmdLineCustomInfoFileName = _T("custom_info_filename");
+
+// The "Embedding" switch indicates that the worker has been invoked to launch
+// the browser. The -Embedding switch is automatically added by COM when
+// launching the COM server.
+const TCHAR* const kCmdLineComServer = _T("Embedding");
+const TCHAR* const kCmdLineComServerDash = _T("-Embedding");
+
+//
+// Legacy support modes
+//
+
+// The legacy "UI" switch supports hand-off machine installs from Omaha 1.
+const TCHAR* const kCmdLineLegacyUi = _T("ui");
+
+// 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 "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");
+
+//
+// Non-product modes
+// These are used for debug, testing, etc.
+//
+
+// Run network diagnostics.
+const TCHAR* const kCmdLineNetDiags = _T("netdiags");
+
+// The "crash" switch indicates that goopdate should crash upon startup.
+// This option is used to test the crash reporting system.
+const TCHAR* const kCmdLineCrash = _T("crash");
+
+//
+// Parameters for other modes
+//
+
+// The "silent" switch specifies that normally interactive modes should run
+// silently.
+const TCHAR* const kCmdLineSilent = _T("silent");
+
+const TCHAR* const kCmdLineOfflineInstall = _T("offlineinstall");
+
+// The "oem" switch specifies that this is an OEM install in Sysprep mode in an
+// OEM factory.
+const TCHAR* const kCmdLineOem = _T("oem");
+
+// The "eularequired" switch specifies that a EULA must be accepted before
+// checking for updates or pinging.
+const TCHAR* const kCmdLineEulaRequired = _T("eularequired");
+
+// The "machine" switch specifies to repair machine Omaha when specified with
+// "recover". Also used to tell the setup phase 2 worker to do a machine install
+// 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 "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.
+const TCHAR* const kCmdLineInstallSource_OneClick = _T("oneclick");
+
+const TCHAR* const kCmdLineInstallSource_OnDemandUpdate = _T("ondemandupdate");
+const TCHAR* const kCmdLineInstallSource_OnDemandCheckForUpdate =
+    _T("ondemandcheckforupdate");
+
+const TCHAR* const kCmdLineInstallSource_ClickOnce = _T("clickonce");
+
+const TCHAR* const kCmdLineInstallSource_Offline = _T("offline");
+
+//
+// "Extra" arguments provided in the metainstaller tag.
+//
+
+// "lang" extra argument tells Omaha the language of the product the user is
+// installing.
+const TCHAR* const kExtraArgLanguage = _T("lang");
+
+// "usagestats" extra argument tells Omaha the user has agreed to provide
+// usage stats, crashreports etc.
+const TCHAR* const kExtraArgUsageStats = _T("usagestats");
+
+// "iid" extra argument is a unique value for this installation session.
+// It can be used to follow the progress from the website to installation
+// completion.
+const TCHAR* const kExtraArgInstallationId = _T("iid");
+
+// "brand" extra argument is the Brand Code used for branding.
+// If a brand value already exists on the system, it is ignored.
+// This value is used to set the initial brand for Omaha and the client app.
+const TCHAR* const kExtraArgBrandCode = _T("brand");
+
+// "client" extra argument is the Client ID used for branding.
+// If a client value already exists on the system, it is ignored.
+// This value is used to set the initial client for Omaha and the client app.
+const TCHAR* const kExtraArgClientId = _T("client");
+
+// "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.
+const TCHAR* const kExtraArgAdditionalParameters = _T("ap");
+
+// "tt_token" 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");
+
+// 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.
+const TCHAR* const kExtraArgAppGuid = _T("appguid");
+const TCHAR* const kExtraArgAppName = _T("appname");
+const TCHAR* const kExtraArgNeedsAdmin = _T("needsadmin");
+const TCHAR* const kExtraArgInstallDataIndex = _T("installdataindex");
+
+// App arguments are arguments explicitly passed on the command line. They are
+// formatted similar to the regular extra args. For example:
+//     /appargs "appguid={GUID}&installerdata=BlahData"
+// Unlike the regular extra args, they are not embedded in the executable.
+const TCHAR* const kCmdLineAppArgs = _T("appargs");
+
+// This switch allows extra data to be communicated to the application
+// installer. The extra data needs to be URL-encoded. The data will be decoded
+// and written to the file, that is then passed in the command line to the
+// application installer in the form "/installerdata=blah.dat". One per
+// application.
+const TCHAR* const kExtraArgInstallerData = _T("installerdata");
+
+//
+// Parsing characters
+//
+
+const TCHAR* const kExtraArgsSeparators        = _T("&");
+const TCHAR* const kDisallowedCharsInExtraArgs = _T("/");
+const TCHAR        kNameValueSeparatorChar     = _T('=');
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONST_CMD_LINE_H__
+
diff --git a/common/const_config.h b/common/const_config.h
new file mode 100644
index 0000000..14e24ad
--- /dev/null
+++ b/common/const_config.h
@@ -0,0 +1,65 @@
+// 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
new file mode 100644
index 0000000..d2cf292
--- /dev/null
+++ b/common/const_debug.h
@@ -0,0 +1,36 @@
+// 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_object_names.h b/common/const_object_names.h
new file mode 100644
index 0000000..80d02c0
--- /dev/null
+++ b/common/const_object_names.h
@@ -0,0 +1,128 @@
+// 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
new file mode 100644
index 0000000..60c29bf
--- /dev/null
+++ b/common/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 =                  30000;      // 30 seconds
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONST_TIMEOUTS_H__
+
diff --git a/common/const_ui.h b/common/const_ui.h
new file mode 100644
index 0000000..b41be7a
--- /dev/null
+++ b/common/const_ui.h
@@ -0,0 +1,24 @@
+// 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
new file mode 100644
index 0000000..9bf366b
--- /dev/null
+++ b/common/const_utils.h
@@ -0,0 +1,85 @@
+// 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 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")
+
+// 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")
+
+// 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");
+
+// IE regkeys.
+#define kRegKeyIeClass \
+    _T("HKCR\\CLSID\\{0002DF01-0000-0000-C000-000000000046}\\LocalServer32")
+#define kRegValueIeClass _T("")
+
+// Firefox regkeys and constants.
+#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")
+
+// Amount of disk space required for program files.
+#ifdef _DEBUG
+// 100 MB since debug usually includes PDBs in addition to huge EXEs.
+#define kSpaceRequiredToInstallProgramFiles (100LL * 1000LL * 1000LL)
+#else
+#define kSpaceRequiredToInstallProgramFiles (10LL * 1000LL * 1000LL)  // 10MB
+#endif
+
+// Preferred amount of disk space for data (choose first location if found).
+#define kSpacePreferredToInstallDataDir (1000LL * 1000LL * 1000LL)
+
+// Amount of disk space required for data (choose first location if
+// could not find a location with the preferred amount of space).
+#define kSpaceRequiredToInstallDataDir (500LL * 1000LL * 1000LL)
+
+// Maximum file size allowed for performing authentication.
+#define kMaxFileSizeForAuthentication (512000000L)    // 512MB
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONST_UTILS_H__
diff --git a/common/constants.h b/common/constants.h
new file mode 100644
index 0000000..3d7f0ef
--- /dev/null
+++ b/common/constants.h
@@ -0,0 +1,315 @@
+// 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 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
+
+// application name (for debugging messages)
+// kAppName == "Google Update"
+#define kAppName          _T(PUBLISHER_NAME_ANSI) _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_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\\")
+
+//
+// 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 kResponseStatusInternalError = _T("error-internal");
+
+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;
+
+// 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 kMinOemModeMs = 72 * 60 * 60 * 1000;  // 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/crc.cc b/common/crc.cc
new file mode 100644
index 0000000..94341f0
--- /dev/null
+++ b/common/crc.cc
@@ -0,0 +1,898 @@
+// 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/crc.h b/common/crc.h
new file mode 100644
index 0000000..26abe59
--- /dev/null
+++ b/common/crc.h
@@ -0,0 +1,140 @@
+// 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 _CRC_H_
+#define _CRC_H_
+
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// This class implements 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.
+// 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.
+
+// A polynomial is represented by the bit pattern formed by its coefficients,
+// but with the highest order bit not stored.
+// The highest degree coefficient is stored in the lowest numbered bit
+// in the the lowest adderessed byte.   Thus, in what follows,
+// the highest degree coeficient that is stored is in the low order bit
+// of "lo" or "*lo".
+
+// Typical usage:
+//
+// // prepare to do 32-bit CRCs using the default polynomial.  No rolling hash.
+// scoped_ptr<CRC> crc(CRC::Default(32, 0));
+// ...
+// uint64 lo;   // declare a lo,hi pair to hold the CRC
+// uint64 hi;
+// crc->Empty(&lo, &hi);      // Initialize to CRC of empty string
+// crc->Extend(&lo, &hi, "hello", 5);     // Get CRC of "hello"
+// ...
+//
+// // prepare to use a 32-bit rolling hash over 6 bytes
+// scoped_ptr<CRC> crc(CRC::Default(32, 6));
+// ...
+// uint64 lo;   // declare a lo,hi pair to hold the CRC
+// uint64 hi;
+// crc->Empty(&lo, &hi);      // Initialize to CRC of empty string
+// crc->Extend(&lo, &hi, data, 6);     // Get CRC of first 6 bytes
+// for (int i = 6; i != sizeof (data); i++) {
+//   crc->Roll(&lo, &hi, data[i-6], data[i]); // Move window by one byte
+//   // lo,hi is CRC of bytes data[i-5...i]
+// }
+// ...
+//
+
+class CRC {
+public:
+ // Initialize all the tables for CRC's of a given bit length "degree"
+ // using a default polynomial of the given length.
+ //
+ // The argument "roll_length" is used by subsequent calls to
+ // Roll().
+ // Returns a handle that MUST NOT be destroyed with delete.
+ // The default polynomials are those in POLYS[8...128].
+ // Handles returned by Default() MUST NOT be deleted.
+ // Identical calls to Default() yield identical handles.
+ static CRC *Default(int degree, size_t roll_length);
+
+ // Initialize all the tables for CRC's of a given bit length "degree"
+ // using an arbitrary CRC polynomial.
+ // Normally, you would use Default() instead of New()---see above.
+ //
+ // Requires that "lo,hi" contain an irreducible polynomial of degree "degree"
+ // Requires 8 <= degree && degree <= 128
+ // Any irreducible polynomial of the correct degree will work.
+ // See the POLYS array for suitable irredicible polynomials.
+ //
+ // The argument "roll_length" is used by subsequent calls to
+ // Roll().
+ // Each call to New() yeilds a pointer to a new object
+ // that may be deallocated with delete.
+ static CRC *New(uint64 lo, uint64 hi, int degree, size_t roll_length);
+
+ virtual ~CRC();
+
+ // Place the CRC of the empty string in "*lo,*hi"
+ virtual void Empty(uint64 *lo, uint64 *hi) const = 0;
+
+ // If "*lo,*hi" is the CRC of bytestring A, place the CRC of
+ // the bytestring formed from the concatenation of A and the "length"
+ // bytes at "bytes" into "*lo,*hi".
+ virtual void Extend(/*INOUT*/ uint64 *lo, /*INOUT*/ uint64 *hi,
+                     const void *bytes, size_t length) const = 0;
+
+ // Equivalent to Extend(lo, hi, bytes, length) where "bytes"
+ // points to an array of "length" zero bytes.
+ virtual void ExtendByZeroes(/*INOUT*/ uint64 *lo, /*INOUT*/ uint64 *hi,
+                             size_t length) const = 0;
+
+ // If "*lo,*hi" is the CRC of a byte string of length "roll_length"
+ // (which is an argument to New() and Default()) that consists of
+ // byte "o_byte" followed by string S, set "*lo,*hi" to the CRC of
+ // the string that consists of S followed by the byte "i_byte".
+ virtual void Roll(/*INOUT*/ uint64 *lo, /*INOUT*/ uint64 *hi,
+                   uint8 o_byte, uint8 i_byte) const = 0;
+
+ // POLYS[] is an array of valid triples that may be given to New()
+ static const struct Poly {
+   uint64 lo;                      // first half suitable CRC polynomial
+   uint64 hi;                      // second half of suitable CRC polynomial
+   int degree;                     // degree of suitable CRC polynomial
+ } *const POLYS;
+ // It is guaranteed that no two entries in POLYS[] are identical,
+ // that POLYS[i] cnotains a polynomial of degree i for 8 <= i <= 128,
+ // that POLYS[0] and POLYS[1] contains polynomials of degree 32,
+ // that POLYS[2] and POLYS[3] contains polynomials of degree 64,
+ // that POLYS[4] and POLYS[5] contains polynomials of degree 96, and
+ // that POLYS[6] and POLYS[7] contains polynomials of degree 128.
+
+ static const int N_POLYS;         // Number of elements in POLYS array.
+
+protected:
+ CRC();      // Clients may not call constructor;
+               // use Default() or New() instead.
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(CRC);
+};
+
+}  // namespace omaha
+
+#endif
diff --git a/common/debug.cc b/common/debug.cc
new file mode 100644
index 0000000..96b6e70
--- /dev/null
+++ b/common/debug.cc
@@ -0,0 +1,1183 @@
+// 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
new file mode 100644
index 0000000..6f0679f
--- /dev/null
+++ b/common/debug.h
@@ -0,0 +1,316 @@
+// 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
new file mode 100644
index 0000000..634a79e
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..36123de
--- /dev/null
+++ b/common/disk.cc
@@ -0,0 +1,396 @@
+// 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;
+}
+
+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 {
+    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/common/disk.h
new file mode 100644
index 0000000..dd59006
--- /dev/null
+++ b/common/disk.h
@@ -0,0 +1,109 @@
+// 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
+
+#ifndef OMAHA_COMMON_DISK_H__
+#define OMAHA_COMMON_DISK_H__
+
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// A constant we use to determine a large drive.
+// Although today this isn't really really large,
+// it is enough to distinguish small, removable
+// drives that are not continually connected to
+// a computer, from the drives that are.
+// In addition to using this constant, we
+// also check if the drive is hot-pluggable.
+const uint64 kLargeDriveSize = 0x00000000ffffffff;
+
+// returns true if the device is an external disk
+// drive typically is something like: \\?\C:
+bool IsDiskExternal(const TCHAR *drive);
+
+//
+// Determines if a drive can be unplugged without manually disabling
+// the drive first.  By default, USB drives are initialized with
+// "surprise removal" enabled, which means they are hot-pluggable.
+//
+// @param drive  The root of the drive, as returned from GetLogicalDriveStrings
+//               e.g. "E:\\".
+//
+// @returns true if the drive is optimized for quick/surprise removal.
+//   If the function returns false, then caching (lazy write) is enabled for
+//   the drive, otherwise it is not.
+//   If an error occurs during this call, the return value will be 'true' since
+//   we always want to treat a drive as hot-pluggable if we're not sure.
+//
+bool IsHotPluggable(const TCHAR* drive);
+
+//
+// @returns true if the specified drive is larger than kLargeDriveSize.
+//
+// @param drive  The root of the drive, as returned from GetLogicalDriveStrings
+//               e.g. "E:\\".
+//
+bool IsLargeDrive(const TCHAR* drive);
+
+// find the first fixed local disk with at least the space requested
+// 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);
+
+// Get free disk space of a drive containing the specified folder
+HRESULT GetFreeDiskSpace(uint32 csidl, uint64* free_disk_space);
+
+// Get free disk space of a drive containing the specified folder
+HRESULT GetFreeDiskSpace(const TCHAR* folder, uint64* free_disk_space);
+
+// Has enough free disk space on a drive containing the specified folder
+HRESULT HasEnoughFreeDiskSpace(uint32 csidl, uint64 disk_space_needed);
+
+// Has enough free disk space on a drive containing the specified folder
+HRESULT HasEnoughFreeDiskSpace(const TCHAR* folder, uint64 disk_space_needed);
+
+// Convert from "\Device\Harddisk0\Partition1\WINNT\System32\ntdll.dll" to
+// "C:\WINNT\System32\ntdll.dll"
+HRESULT DevicePathToDosPath(const TCHAR* device_path, CString* dos_path);
+
+//
+// Disables critical error dialogs on the current thread.
+// The system does not display the critical-error-handler message box.
+// Instead, the system returns the error to the calling process.
+//
+class DisableThreadErrorUI {
+ public:
+  DisableThreadErrorUI() {
+    // Set the error mode
+    prev_mode_ = SetErrorMode(SEM_FAILCRITICALERRORS);
+  }
+
+  ~DisableThreadErrorUI() {
+    // Restore the error mode
+    SetErrorMode(prev_mode_);
+  }
+
+ protected:
+  UINT prev_mode_;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_DISK_H__
+
diff --git a/common/disk_unittest.cc b/common/disk_unittest.cc
new file mode 100644
index 0000000..d44fb6e
--- /dev/null
+++ b/common/disk_unittest.cc
@@ -0,0 +1,73 @@
+// 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 <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, Disk) {
+  uint64 bytes_available = 0;
+  ASSERT_SUCCEEDED(GetFreeDiskSpace(_T("C:\\"), &bytes_available));
+
+  bytes_available = 0;
+  ASSERT_SUCCEEDED(GetFreeDiskSpace(CSIDL_PROGRAM_FILES, &bytes_available));
+
+  TCHAR system_drive[MAX_PATH] = _T("%SystemDrive%");
+  ASSERT_TRUE(::ExpandEnvironmentStrings(system_drive, system_drive, MAX_PATH));
+  ASSERT_TRUE(::PathAddBackslash(system_drive));
+
+  if (vista_util::IsUserAdmin()) {
+    // System drive should not be hot-pluggable
+    ASSERT_FALSE(IsHotPluggable(system_drive));
+  }
+
+  // System drive is expected to be > 4GB.
+  ASSERT_TRUE(IsLargeDrive(system_drive));
+
+  CString drive;
+  ASSERT_SUCCEEDED(FindFirstLocalDriveWithEnoughSpace(
+      kSpacePreferredToInstallDataDir, &drive));
+  if (vista_util::IsUserAdmin()) {
+    ASSERT_FALSE(IsHotPluggable(drive));
+  }
+  ASSERT_TRUE(IsLargeDrive(drive));
+}
+
+#if 0
+// http://b/1076675: test fails when run on mapped drives
+TEST(DiskTest, DevicePathToDosPath) {
+  TCHAR image_name[MAX_PATH] = _T("");
+  ASSERT_TRUE(::GetProcessImageFileName(::GetCurrentProcess(),
+                                        image_name,
+                                        arraysize(image_name)) != 0);
+
+  CString dos_name;
+  ASSERT_SUCCEEDED(DevicePathToDosPath(image_name, &dos_name));
+  ASSERT_TRUE(File::Exists(dos_name));
+
+  ASSERT_EQ(dos_name.CompareNoCase(app_util::GetCurrentModulePath()), 0);
+}
+#endif
+
+}  // namespace omaha
+
diff --git a/common/dynamic_link_dbghelp.cc b/common/dynamic_link_dbghelp.cc
new file mode 100644
index 0000000..1d54d93
--- /dev/null
+++ b/common/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/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_dbghelp.h b/common/dynamic_link_dbghelp.h
new file mode 100644
index 0000000..df6de46
--- /dev/null
+++ b/common/dynamic_link_dbghelp.h
@@ -0,0 +1,70 @@
+// 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
+//
+
+#ifndef OMAHA_COMMON_DYNAMIC_LINK_DBGHELP_H_
+#define OMAHA_COMMON_DYNAMIC_LINK_DBGHELP_H_
+
+#include <dbghelp.h>
+
+namespace omaha {
+
+class Dbghelp {
+ public:
+  static BOOL (CALLBACK *SymInitialize)(HANDLE, PCSTR, BOOL);
+  static BOOL (CALLBACK *SymCleanup)(HANDLE);
+  static BOOL (CALLBACK *SymEnumSymbols)(HANDLE, ULONG64, PCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID);
+  static DWORD (CALLBACK *SymSetOptions)(DWORD);
+  static BOOL (CALLBACK *SymSetContext)(HANDLE, PIMAGEHLP_STACK_FRAME, PIMAGEHLP_CONTEXT);
+  static BOOL (CALLBACK *SymGetLineFromAddr)(HANDLE, DWORD, PDWORD, PIMAGEHLP_LINE);
+  static BOOL (CALLBACK *SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
+  static BOOL (CALLBACK *SymFromAddr)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
+  static BOOL (CALLBACK *StackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,
+      PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
+  static BOOL (CALLBACK *StackWalk)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, PVOID, PREAD_PROCESS_MEMORY_ROUTINE,
+      PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
+  static PVOID (CALLBACK *SymFunctionTableAccess64)(HANDLE, DWORD64);
+  static PVOID (CALLBACK *SymFunctionTableAccess)(HANDLE, DWORD);
+  static DWORD64 (CALLBACK *SymGetModuleBase64)(HANDLE, DWORD64);
+  static DWORD64 (CALLBACK *SymGetModuleBase)(HANDLE, DWORD);
+  static BOOL (CALLBACK *SymGetTypeInfo)(HANDLE, DWORD64, ULONG, IMAGEHLP_SYMBOL_TYPE_INFO, PVOID);
+  static BOOL (CALLBACK *MiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION,
+      PMINIDUMP_USER_STREAM_INFORMATION, PMINIDUMP_CALLBACK_INFORMATION);
+
+  static HRESULT Load();
+  static void Unload();
+  static bool Loaded() { return (loaded_state_ == LOAD_SUCCEEDED && library_ != NULL); }
+
+ private:
+  // If we tried to load and failed, do not try again.
+  static enum LoadedState {NOT_LOADED, LOAD_FAILED, LOAD_SUCCEEDED};
+
+  static LoadedState loaded_state_;
+  static HINSTANCE library_;
+
+  static void Clear();
+
+  // wrapper around GetProcAddress()
+  template <typename T>
+  static bool GPA(const char * function_name, T& function_pointer);
+
+  DISALLOW_EVIL_CONSTRUCTORS(Dbghelp);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_DYNAMIC_LINK_DBGHELP_H_
diff --git a/common/dynamic_link_kernel32.cc b/common/dynamic_link_kernel32.cc
new file mode 100644
index 0000000..c440479
--- /dev/null
+++ b/common/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/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.h b/common/dynamic_link_kernel32.h
new file mode 100644
index 0000000..b8542f8
--- /dev/null
+++ b/common/dynamic_link_kernel32.h
@@ -0,0 +1,46 @@
+// 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 not supported
+// on Windows 95/98/ME.
+
+#ifndef OMAHA_COMMON_DYNAMIC_LINK_KERNEL32_H_
+#define OMAHA_COMMON_DYNAMIC_LINK_KERNEL32_H_
+
+#include <windows.h>
+#include <tlhelp32.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class Kernel32 {
+ public:
+
+  static BOOL WINAPI Module32First(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+  static BOOL WINAPI Module32Next(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+  static BOOL WINAPI Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
+  static BOOL WINAPI Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
+
+  // Is process running under WOW64?
+  // WOW64 is the emulator for win32 applications running on win64.
+  static BOOL WINAPI IsWow64Process(HANDLE hProcess, PBOOL Wow64Process);
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(Kernel32);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_DYNAMIC_LINK_KERNEL32_H_
diff --git a/common/dynamic_link_kernel32_unittest.cc b/common/dynamic_link_kernel32_unittest.cc
new file mode 100644
index 0000000..6df570c
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..4d5a758
--- /dev/null
+++ b/common/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/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.h b/common/encrypt.h
new file mode 100644
index 0000000..59a43de
--- /dev/null
+++ b/common/encrypt.h
@@ -0,0 +1,46 @@
+// 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.
+// ========================================================================
+//
+// Encryption functions
+
+#ifndef OMAHA_COMMON_ENCRYPT_H__
+#define OMAHA_COMMON_ENCRYPT_H__
+
+#include <windows.h>
+#include <wincrypt.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+namespace encrypt {
+
+// Encrypts a data buffer by using the user's credentials as well as
+// the provided key. The key may be NULL, if no entropy is needed.
+HRESULT EncryptData(const void* key, size_t key_len,
+                    const void* data, size_t data_len,
+                    std::vector<uint8>* data_out);
+
+// Decrypts the provided data buffer. The key may be NULL, if no entropy
+// is needed.
+HRESULT DecryptData(const void* key, size_t key_len,
+                    const void* data, size_t data_len,
+                    std::vector<uint8>* data_out);
+
+}  // namespace encrypt
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_ENCRYPT_H__
diff --git a/common/encrypt_test.cc b/common/encrypt_test.cc
new file mode 100644
index 0000000..55ca9b2
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..1838882
--- /dev/null
+++ b/common/error.cc
@@ -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.
+// ========================================================================
+
+#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
new file mode 100644
index 0000000..ff6dce3
--- /dev/null
+++ b/common/error.h
@@ -0,0 +1,430 @@
+// 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.
+// ========================================================================
+//
+// 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)
+#define GOOPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED_TIMESTAMP_CHECK \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 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)
+#define GOOPDATE_E_NON_OEM_INSTALL_IN_AUDIT_MODE    \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 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)
+
+//
+// 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
new file mode 100644
index 0000000..b9df549
--- /dev/null
+++ b/common/error_unittest.cc
@@ -0,0 +1,38 @@
+// 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_handler.h b/common/event_handler.h
new file mode 100644
index 0000000..1a0990c
--- /dev/null
+++ b/common/event_handler.h
@@ -0,0 +1,36 @@
+// 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_EVENT_HANDLER_H_
+#define OMAHA_COMMON_EVENT_HANDLER_H_
+
+#include <windows.h>
+
+namespace omaha {
+
+// Defines an abstract interface for the event handlers associated with
+// waitable kernel objects.
+class EventHandler {
+ public:
+  virtual ~EventHandler() {}
+
+  // Gets called when the handle is signaled.
+  virtual void HandleEvent(HANDLE h) = 0;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_EVENT_HANDLER_H_
+
diff --git a/common/exception_barrier.cc b/common/exception_barrier.cc
new file mode 100644
index 0000000..526cc55
--- /dev/null
+++ b/common/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/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/exception_barrier.h b/common/exception_barrier.h
new file mode 100644
index 0000000..ecc01b6
--- /dev/null
+++ b/common/exception_barrier.h
@@ -0,0 +1,110 @@
+// 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.
+#ifndef OMAHA_COMMON_EXCEPTION_BARRIER_H_
+#define OMAHA_COMMON_EXCEPTION_BARRIER_H_
+
+#include <windows.h>
+
+/// This is the type dictated for an exception handler by the platform ABI
+/// @see _except_handler in excpt.h
+typedef EXCEPTION_DISPOSITION (__cdecl *ExceptionHandlerFunc)(
+                                struct _EXCEPTION_RECORD *exception_record,
+                                void * establisher_frame,
+                                struct _CONTEXT *context,
+                                void * reserved);
+
+/// The type of an exception record in the exception handler chain
+struct EXCEPTION_REGISTRATION {
+  EXCEPTION_REGISTRATION *prev;
+  ExceptionHandlerFunc  handler;
+};
+
+/// This is our raw exception handler, it must be declared 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);
+
+/// An exception barrier is used to report exceptions that pass through
+/// a boundary where exceptions shouldn't pass, such as e.g. COM interface
+/// boundaries.
+/// This is handy for any kind of plugin code, where if the exception passes
+/// through unhindered, it'll either be swallowed by an SEH exception handler
+/// above us on the stack, or be reported as an unhandled exception for
+/// the application hosting the plugin code.
+///
+/// To use this class, simply instantiate an ExceptionBarrier just inside
+/// the code boundary, like this:
+/// @code
+/// HRESULT SomeObject::SomeCOMMethod(...) {
+///   ExceptionBarrier report_crashes;
+///
+///   ... other code here ...
+/// }
+/// @endcode
+class ExceptionBarrier {
+ public:
+  /// Register the barrier in the SEH chain
+  ExceptionBarrier();
+
+  /// And unregister on destruction
+  ~ExceptionBarrier();
+
+  /// Signature of the handler function which gets notified when
+  /// an exception propagates through a barrier.
+  typedef void (CALLBACK *ExceptionHandler)(EXCEPTION_POINTERS *ptrs);
+
+  /// @name Accessors
+  /// @{
+  static void set_handler(ExceptionHandler handler) { s_handler_ = handler; }
+  static ExceptionHandler handler() { return s_handler_; }
+  /// @}
+
+ private:
+  /// Our SEH frame
+  EXCEPTION_REGISTRATION registration_;
+
+  /// The function that gets invoked if an exception
+  /// propagates through a barrier
+  /// TODO(omaha): how can this be statically parametrized?
+  static ExceptionHandler s_handler_;
+};
+
+/// @name These are implemented in the associated .asm file
+/// @{
+extern "C" void WINAPI RegisterExceptionRecord(
+                          EXCEPTION_REGISTRATION *registration,
+                          ExceptionHandlerFunc func);
+extern "C" void WINAPI UnregisterExceptionRecord(
+                          EXCEPTION_REGISTRATION *registration);
+/// @}
+
+
+inline ExceptionBarrier::ExceptionBarrier() {
+  RegisterExceptionRecord(&registration_, ExceptionBarrierHandler);
+}
+
+inline ExceptionBarrier::~ExceptionBarrier() {
+  // TODO(omaha): I don't think it's safe to unregister after an exception
+  //          has taken place???
+  UnregisterExceptionRecord(&registration_);
+}
+
+#endif  // OMAHA_COMMON_EXCEPTION_BARRIER_H_
diff --git a/common/exception_barrier_lowlevel.asm b/common/exception_barrier_lowlevel.asm
new file mode 100644
index 0000000..0906b92
--- /dev/null
+++ b/common/exception_barrier_lowlevel.asm
@@ -0,0 +1,63 @@
+; 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.
+; ========================================================================
+;
+; Tag the exception handler as an SEH handler in case the executable
+; is linked with /SAFESEH (which is the default).
+;
+; MASM 8.0 inserts an additional leading underscore in front of names
+; and this is an attempted fix until we understand why.
+IF @version LT 800
+_ExceptionBarrierHandler PROTO
+.SAFESEH _ExceptionBarrierHandler
+ELSE
+ExceptionBarrierHandler PROTO
+.SAFESEH ExceptionBarrierHandler
+ENDIF
+
+.586
+.MODEL FLAT, STDCALL
+ASSUME FS:NOTHING
+.CODE
+
+; extern "C" void WINAPI RegisterExceptionRecord(
+;                          EXCEPTION_REGISTRATION *registration,
+;                          ExceptionHandlerFunc func);
+RegisterExceptionRecord PROC registration:DWORD, func:DWORD
+OPTION PROLOGUE:None
+OPTION EPILOGUE:None
+  mov   edx, DWORD PTR [esp + 4]  ; edx is registration
+  mov   eax, DWORD PTR [esp + 8] ; eax is func
+  mov   DWORD PTR [edx + 4], eax
+  mov   eax, FS:[0]
+  mov   DWORD PTR [edx], eax
+  mov   FS:[0], edx
+  ret   8
+
+RegisterExceptionRecord ENDP
+
+; extern "C" void UnregisterExceptionRecord(
+;                           EXCEPTION_REGISTRATION *registration);
+UnregisterExceptionRecord PROC registration:DWORD
+OPTION PROLOGUE:None
+OPTION EPILOGUE:None
+
+  mov   edx, DWORD PTR [esp + 4]
+  mov   eax, [edx]
+  mov   FS:[0], eax
+  ret   4
+
+UnregisterExceptionRecord ENDP
+
+END
diff --git a/common/exception_utils.cc b/common/exception_utils.cc
new file mode 100644
index 0000000..56af779
--- /dev/null
+++ b/common/exception_utils.cc
@@ -0,0 +1,86 @@
+// 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 "exception_utils.h"
+
+
+// Capture a CONTEXT that represents machine state at callsite
+// TODO(omaha): 64 bit platforms can pass through to
+//      RtlCaptureContext (or can they?)
+__declspec(naked) PCONTEXT WINAPI CaptureContext(PCONTEXT runner) {
+  runner;   // unreferenced formal parameter
+  __asm {
+    // set up a call frame
+    push ebp
+    mov ebp, esp
+
+    // save ecx for later
+    push ecx
+
+    // fetch the context record pointer argument into ecx
+    // which we use as pointer to context throughout the rest
+    // of this function
+    mov ecx, DWORD PTR [ebp + 8]
+
+    // set flags
+    mov [ecx]CONTEXT.ContextFlags, CONTEXT_SEGMENTS | CONTEXT_INTEGER | \
+                                   CONTEXT_CONTROL | CONTEXT_FLOATING_POINT
+
+    // stash the integer registers away
+    mov [ecx]CONTEXT.Edi, edi
+    mov [ecx]CONTEXT.Ebx, ebx
+    mov [ecx]CONTEXT.Edx, edx
+    mov [ecx]CONTEXT.Eax, eax
+    mov [ecx]CONTEXT.Esi, esi
+    // get the saved ecx
+    pop eax
+    mov [ecx]CONTEXT.Ecx, eax
+
+    // now control registers
+    pushfd
+    pop eax
+    mov [ecx]CONTEXT.EFlags, eax
+
+    // get the old ebp, our FP points to it
+    mov eax, [ebp]
+    mov [ecx]CONTEXT.Ebp, eax
+
+    // get return address and record as eip
+    mov eax, [ebp + 4]
+    mov [ecx]CONTEXT.Eip, eax
+
+    // esp post-return is ...
+    lea eax, [ebp + 0xC]
+    mov [ecx]CONTEXT.Esp, eax
+
+    // snarf segment registers
+    mov word ptr [ecx]CONTEXT.SegSs, ss
+    mov word ptr [ecx]CONTEXT.SegCs, cs
+    mov word ptr [ecx]CONTEXT.SegGs, gs
+    mov word ptr [ecx]CONTEXT.SegFs, fs
+    mov word ptr [ecx]CONTEXT.SegEs, es
+    mov word ptr [ecx]CONTEXT.SegDs, ds
+
+    // and lastly grab floating point state
+    fnsave [ecx]CONTEXT.FloatSave
+
+    // return the CONTEXT pointer
+    mov eax, ecx
+
+    // and return
+    pop ebp
+    ret 4
+  }
+}
diff --git a/common/exception_utils.h b/common/exception_utils.h
new file mode 100644
index 0000000..9ee9005
--- /dev/null
+++ b/common/exception_utils.h
@@ -0,0 +1,25 @@
+// 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.
+// ========================================================================
+//
+// Utility functions for crash reporting
+#ifndef OMAHA_COMMON_EXCEPTION_UTILS_H_
+#define OMAHA_COMMON_EXCEPTION_UTILS_H_
+
+#include <windows.h>
+
+/// Captures machine state at callsite.
+PCONTEXT WINAPI CaptureContext(PCONTEXT runner);
+
+#endif  // OMAHA_COMMON_EXCEPTION_UTILS_H_
diff --git a/common/extractor.cc b/common/extractor.cc
new file mode 100644
index 0000000..a315ab5
--- /dev/null
+++ b/common/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/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.h b/common/extractor.h
new file mode 100644
index 0000000..21a8def
--- /dev/null
+++ b/common/extractor.h
@@ -0,0 +1,105 @@
+// 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_EXTRACTOR_H_
+#define OMAHA_COMMON_EXTRACTOR_H_
+
+#include <windows.h>
+
+namespace omaha {
+
+class TagExtractor {
+ public:
+  TagExtractor();
+  virtual ~TagExtractor();
+
+    /**
+    * @return true if we successfully opened the file.
+    */
+    bool OpenFile(const TCHAR* filename);
+
+    /**
+    * @return true if we currently have a handle to an open file.
+    */
+    bool IsFileOpen() const;
+
+    void CloseFile();
+
+    /**
+    * Returns the tag in the current file.
+    *
+    * We're exploiting the empirical observation that Windows checks the
+    * signature on a PEF but doesn't care if the signature container includes
+    * extra bytes after the signature.
+    *
+    * Logic:
+    *
+    *   - Sanity-check that we're a PEF image.
+    *   - Find the signature, which should be stored in the PE "Certificates
+    *     Directory" (dumpbin.exe /headers "Firefox Setup 1.0.7.exe") in a
+    *     WIN_CERTIFICATE structure.
+    *   - Crudely parse the ASN.1 signature to determine its end.
+    *   - Read the signature starting from the first byte past the ASN.1.
+    *
+    * @param tag_buffer: a buffer that will be filled with the extracted tag as
+    *   a null-terminated string, or NULL if the caller doesn't want the tag.
+    *
+    * @param tag_buffer_len: a pointer to an int that represents the length in
+    *   bytes of the buffer pointed to by tag_buffer. If tag_buffer is NULL and
+    *   there is a tag to extract, then we fill this int with the size of the
+    *   smallest buffer needed to contain the tag (plus the null terminator).
+    *
+    * @return true if we found a tag and either successfully copied all of it
+    *   into tag_buffer, or tag_buffer was NULL and we successfully returned
+    *   the required buffer size in tag_buffer_len.
+    */
+    bool ExtractTag(char* tag_buffer, int* tag_buffer_len);
+    bool ExtractTag(const char* binary_file,
+                    size_t binary_file_length,
+                    char* tag_buffer,
+                    int* tag_buffer_len);
+
+    int cert_length() const { return cert_length_; }
+    const void* cert_dir_base() const { return cert_dir_base_; }
+
+ private:
+  HANDLE file_handle_;
+  HANDLE file_mapping_;
+  LPVOID file_base_;
+  size_t file_length_;
+  int cert_length_;
+  const void* cert_dir_base_;
+
+  bool ReadTag(const char* tag_pointer,
+               char* tag_buffer,
+               int* tag_buffer_len) const;
+
+  const void* GetCertificateDirectoryPointer(const void* base) const;
+
+  const void* GetASN1SignaturePointer(const void* base) const;
+
+  int GetASN1SignatureLength(const void* base) const;
+
+  bool InternalExtractTag(const char* file_buffer,
+                          char* tag_buffer,
+                          int* tag_buffer_len);
+
+  bool InternalReadCertificate(const char* file_buffer);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_EXTRACTOR_H_
diff --git a/common/extractor_unittest.cc b/common/extractor_unittest.cc
new file mode 100644
index 0000000..9b46089
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..da25b7d
--- /dev/null
+++ b/common/file.cc
@@ -0,0 +1,1342 @@
+// 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;
+}
+
+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
new file mode 100644
index 0000000..060b63c
--- /dev/null
+++ b/common/file.h
@@ -0,0 +1,251 @@
+// 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
new file mode 100644
index 0000000..9f84dc3
--- /dev/null
+++ b/common/file_reader.cc
@@ -0,0 +1,247 @@
+// 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_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
new file mode 100644
index 0000000..c97c3e9
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..af62b00
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..85b2dc0
--- /dev/null
+++ b/common/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/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.h b/common/file_store.h
new file mode 100644
index 0000000..43febe9
--- /dev/null
+++ b/common/file_store.h
@@ -0,0 +1,62 @@
+// 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
+
+#ifndef OMAHA_COMMON_FILE_STORE_H__
+#define OMAHA_COMMON_FILE_STORE_H__
+
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class FileStore {
+ public:
+  // Open the store
+  bool Open(const TCHAR* file_path);
+
+  // Close the store
+  bool Close();
+
+  // Clear the store
+  bool Clear();
+
+  // Read a value from the store
+  bool Read(const TCHAR* name, std::vector<byte>* data) const;
+
+  // Write a value to the store
+  bool Write(const TCHAR* name, byte* data, int data_size);
+
+  // Check to see a named value exists in the store
+  bool Exists(const TCHAR* name) const;
+
+  // Remove a value from the store
+  bool Remove(const TCHAR* name);
+
+  // Get the number of values for this store
+  bool GetValueCount(uint32* value_count);
+
+  // Get the value name for the given value name index
+  bool GetValueNameAt(uint32 index, CString* value_name);
+
+ private:
+  CString file_path_;      // Full path to the file store
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_FILE_STORE_H__
diff --git a/common/file_store_unittest.cc b/common/file_store_unittest.cc
new file mode 100644
index 0000000..eb127de
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..4e16a24
--- /dev/null
+++ b/common/file_unittest.cc
@@ -0,0 +1,369 @@
+// 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
+
+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));
+}
+
+}  // namespace omaha
+
diff --git a/common/file_ver.cc b/common/file_ver.cc
new file mode 100644
index 0000000..6aed887
--- /dev/null
+++ b/common/file_ver.cc
@@ -0,0 +1,177 @@
+// 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/file_ver.h b/common/file_ver.h
new file mode 100644
index 0000000..6ba29f0
--- /dev/null
+++ b/common/file_ver.h
@@ -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.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_FILE_VER_H_
+#define OMAHA_COMMON_FILE_VER_H_
+
+#include <windows.h>
+#include <tchar.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class FileVer {
+ public:
+  FileVer();
+  ~FileVer();
+
+  // opens the version info for the specified file
+  BOOL Open(const TCHAR* lpszModuleName);
+
+  // Cleanup
+  void Close();
+
+  // Query for a given vlaue
+  CString QueryValue(const TCHAR* lpszValueName) const;
+
+  // Shortcuts for common values
+  CString GetFileDescription() const {return QueryValue(_T("FileDescription"));}
+  CString GetFileVersion() const     {return QueryValue(_T("FileVersion"));    }
+  CString GetCompanyName() const     {return QueryValue(_T("CompanyName"));    }
+  CString GetProductName() const     {return QueryValue(_T("ProductName"));    }
+  CString GetProductVersion() const  {return QueryValue(_T("ProductVersion")); }
+
+  // gets the FIXEDFILEINFO datastructure
+  BOOL GetFixedInfo(VS_FIXEDFILEINFO& vsffi) const;   // NOLINT
+
+  // returns a formated string representing the file and product versions
+  // e.g. 2.4.124.34
+  CString FormatFixedFileVersion() const;
+  CString FormatFixedProductVersion() const;
+
+  // Returns a ULONGLONG containing the version in DLL version format.
+  ULONGLONG GetFileVersionAsULONGLONG() const;
+
+  // gets the language ID
+  LCID GetLanguageID() const         { return HIWORD(lang_charset_); }
+
+ private:
+  // versioning data returned by GetFileVersionInfo
+  byte*  file_ver_data_;
+
+  // language charset
+  DWORD   lang_charset_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(FileVer);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_FILE_VER_H_
diff --git a/common/firewall_product_detection.cc b/common/firewall_product_detection.cc
new file mode 100644
index 0000000..cc32cfa
--- /dev/null
+++ b/common/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/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.h b/common/firewall_product_detection.h
new file mode 100644
index 0000000..d29e786
--- /dev/null
+++ b/common/firewall_product_detection.h
@@ -0,0 +1,33 @@
+// 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_FIREWALL_PRODUCT_DETECTION_H_
+#define OMAHA_COMMON_FIREWALL_PRODUCT_DETECTION_H_
+
+#include <windows.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+namespace firewall_detection {
+
+// Detects if the computer is running a software firewall.
+HRESULT Detect(CString* name, CString* version);
+
+}  // namespace firewall_detection
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_FIREWALL_PRODUCT_DETECTION_H_
diff --git a/common/firewall_product_detection_unittest.cc b/common/firewall_product_detection_unittest.cc
new file mode 100644
index 0000000..fe440ee
--- /dev/null
+++ b/common/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/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_appid.rgs b/common/generic_reg_file_appid.rgs
new file mode 100644
index 0000000..829d0b2
--- /dev/null
+++ b/common/generic_reg_file_appid.rgs
@@ -0,0 +1,18 @@
+HKLM
+{
+  NoRemove SOFTWARE
+  {
+    NoRemove Classes
+    {
+      NoRemove AppID
+      {
+        ForceRemove '%APPID%' = s '%DESCRIPTION%'
+        ForceRemove '%FILENAME%'
+        {
+          val AppID = s '%APPID%'
+        }
+      }
+    }
+  }
+}
+
diff --git a/common/generic_reg_file_elevation_localserver.rgs b/common/generic_reg_file_elevation_localserver.rgs
new file mode 100644
index 0000000..15aeb12
--- /dev/null
+++ b/common/generic_reg_file_elevation_localserver.rgs
@@ -0,0 +1,35 @@
+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
new file mode 100644
index 0000000..d98b665
--- /dev/null
+++ b/common/generic_reg_file_local_server.rgs
@@ -0,0 +1,29 @@
+%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
new file mode 100644
index 0000000..f3be278
--- /dev/null
+++ b/common/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/common/generic_reg_file_localservice.rgs b/common/generic_reg_file_localservice.rgs
new file mode 100644
index 0000000..77eaf13
--- /dev/null
+++ b/common/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/common/generic_reg_file_typelib.rgs b/common/generic_reg_file_typelib.rgs
new file mode 100644
index 0000000..909aa96
--- /dev/null
+++ b/common/generic_reg_file_typelib.rgs
@@ -0,0 +1,10 @@
+HKCR
+{
+  NoRemove CLSID
+  {
+    ForceRemove %CLSID% = s '%DESCRIPTION%'
+    {
+      'TypeLib' = s '%LIBID%'
+    }
+  }
+}
diff --git a/common/google_update_recovery.cc b/common/google_update_recovery.cc
new file mode 100644
index 0000000..ea6ae66
--- /dev/null
+++ b/common/google_update_recovery.cc
@@ -0,0 +1,589 @@
+// 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.
+// ========================================================================
+
+//
+// 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"
+#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&machineid=%s&userid=%s")
+    _T("&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 kRegValueUserId          = _T("ui");
+const TCHAR* const kRegValueMachineId       = _T("mi");
+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 E_FAIL;
+  }
+
+  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);
+  if (ERROR_SUCCESS == res) {
+    local_value.ReleaseBufferSetLength(byte_count / sizeof(TCHAR));
+    *value = local_value;
+  }
+
+  ::RegCloseKey(key);
+  return HRESULT_FROM_WIN32(res);
+}
+
+// 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,
+                            CString* machine_id,
+                            CString* user_id) {
+  if (!omaha_version || !machine_id || !user_id) {
+    return E_INVALIDARG;
+  }
+
+  if (FAILED(GetRegStringValue(is_machine_app,
+                               kRelativeClientsGoopdateRegPath,
+                               kRegValueProductVersion,
+                               omaha_version))) {
+    *omaha_version = _T("0.0.0.0");
+  }
+
+  GetRegStringValue(true,  // Machine ID is always in HKLM.
+                    kRelativeGoopdateRegPath,
+                    kRegValueMachineId,
+                    machine_id);
+
+  GetRegStringValue(is_machine_app,
+                    kRelativeGoopdateRegPath,
+                    kRegValueUserId,
+                    user_id);
+
+  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;
+  CString machine_id;
+  CString user_id;
+  GetOmahaInformation(is_machine_app, &omaha_version, &machine_id, &user_id);
+
+  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 machine_id_escaped;
+  CString user_id_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(machine_id, true, &machine_id_escaped);
+  StringEscape(user_id, true, &user_id_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,
+                machine_id_escaped,
+                user_id_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 and that it has not expired.
+  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.
+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;
+  }
+
+  CString download_target_path;
+  CString temp_file_path;
+  HRESULT 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
new file mode 100644
index 0000000..a096c4e
--- /dev/null
+++ b/common/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_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
new file mode 100644
index 0000000..2934b10
--- /dev/null
+++ b/common/google_update_recovery_unittest.cc
@@ -0,0 +1,785 @@
+// 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 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/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"
+
+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 kDummyMachineId[] = _T("{12A7B304-B4ED-4b5c-8122-31E7ECB8BE3C}");
+const TCHAR kDummyUserId[] = _T("{29388E49-2995-437c-BF47-587C7E9247E5}");
+
+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\\");
+
+// 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;
+  ASSERT_HRESULT_SUCCEEDED(
+      machine_key.Create(hive_override_key_name + MACHINE_KEY));
+  ASSERT_HRESULT_SUCCEEDED(user_key.Create(hive_override_key_name + USER_KEY));
+  ASSERT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_LOCAL_MACHINE,
+                                                  machine_key.Key()));
+  ASSERT_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() {
+  ASSERT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_LOCAL_MACHINE, NULL));
+  ASSERT_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);
+    }
+    ASSERT_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
+    ASSERT_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();
+    ASSERT_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);
+  ASSERT_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);
+  ASSERT_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&machineid=%7B12A7B304-B4ED-4b5c-8122-31E7ECB8BE3C%7D&userid=%7B29388E49-2995-437c-BF47-587C7E9247E5%7D&osversion=");  // NOLINT
+
+  const CString prev_tmp = GetTmp();
+  ASSERT_TRUE(::SetEnvironmentVariable(_T("TMP"), kTempDirectory));
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullMachineOmahaClientKeyPath,
+                                            _T("pv"),
+                                            _T("5.6.78.1")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullMachineOmahaMainKeyPath,
+                                            _T("mi"),
+                                            kDummyMachineId));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullMachineOmahaMainKeyPath,
+                                            _T("ui"),
+                                            kDummyUserId));
+
+  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&machineid=%7B12A7B304-B4ED-4b5c-8122-31E7ECB8BE3C%7D&userid=%7B29388E49-2995-437c-BF47-587C7E9247E5%7D&osversion=");  // NOLINT
+
+  const CString prev_tmp = GetTmp();
+  ASSERT_TRUE(::SetEnvironmentVariable(_T("TMP"), kTempDirectory));
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullUserOmahaClientKeyPath,
+                                            _T("pv"),
+                                            _T("5.6.78.1")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullMachineOmahaMainKeyPath,
+                                            _T("mi"),
+                                            kDummyMachineId));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullUserOmahaMainKeyPath,
+                                            _T("ui"),
+                                            kDummyUserId));
+
+  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) {
+  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=0.0.0.0&machineid=&userid=&osversion=");  // NOLINT
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrl, saved_url_.Left(arraysize(kExpectedUrl) - 1));
+  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&machineid=&userid=&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));
+}
+
+// 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);
+  ASSERT_FALSE(File::Exists(kNonExistantDirectory));
+
+  const CString prev_tmp = GetTmp();
+  ASSERT_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));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileCollision) {
+  const CString prev_tmp = GetTmp();
+  ASSERT_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());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kArgumentSavingExecutableRelativePath));
+  ASSERT_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());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kUnsignedExecutable));
+  ASSERT_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 kUnsignedExecutable[] =
+      _T("unittest_support\\GoogleUpdate_old_signature.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kUnsignedExecutable));
+  ASSERT_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.
+// The error returned is inappropriate for this case because of the way
+// expiration checking is implemented.
+TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_SignedWithNowExpiredCert) {
+  const TCHAR kUnsignedExecutable[] =
+      _T("unittest_support\\GoogleUpdate_now_expired_cert.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kUnsignedExecutable));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_EQ(CERT_E_CN_NO_MATCH, 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());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kUntrustedChainExecutable));
+  ASSERT_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());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kCorruptedExecutable));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_EQ(TRUST_E_BAD_DIGEST, VerifyFileSignature(executable_full_path));
+}
+
+TEST_F(GoogleUpdateRecoveryTest,
+       VerifyFileSignature_NonGoogleSignature) {
+  CString file_path = SystemInfo::IsRunningOnVistaOrLater() ?
+      _T("%SYSTEM%\\rcagent.exe") : _T("%SYSTEM%\\wuauclt.exe");
+  ASSERT_HRESULT_SUCCEEDED(ExpandStringWithSpecialFolders(&file_path));
+  ASSERT_TRUE(File::Exists(file_path));
+  ASSERT_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("RecoveryTest.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/has_exception_namespace_fix.h b/common/has_exception_namespace_fix.h
new file mode 100644
index 0000000..e4f6367
--- /dev/null
+++ b/common/has_exception_namespace_fix.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.
+// ========================================================================
+
+#if !_HAS_EXCEPTIONS
+// Fixes a problem with using the standard library with exceptions turned off.
+// See discussion at http://tiny.cc/byLhe
+#include <exception>
+using std::exception;
+#endif
diff --git a/common/highres_timer-win32.cc b/common/highres_timer-win32.cc
new file mode 100644
index 0000000..f4597f3
--- /dev/null
+++ b/common/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/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-win32.h b/common/highres_timer-win32.h
new file mode 100644
index 0000000..68a9f6e
--- /dev/null
+++ b/common/highres_timer-win32.h
@@ -0,0 +1,90 @@
+// 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_HIGHRES_TIMER_WIN32_H__
+#define OMAHA_COMMON_HIGHRES_TIMER_WIN32_H__
+
+#include <windows.h>
+
+namespace omaha {
+
+/// A handy class for reliably measuring wall-clock time with decent resolution,
+/// even on multi-processor machines and on laptops (where RDTSC potentially
+/// returns different results on different processors and/or the RDTSC timer
+/// clocks at different rates depending on the power state of the CPU,
+/// respectively).
+class HighresTimer {
+ public:
+  /// Captures the current start time
+  HighresTimer();
+
+  /// Captures the current tick, can be used to reset a timer for reuse.
+  void Start();
+
+  /// Returns the elapsed ticks with full resolution
+  ULONGLONG GetElapsedTicks() const;
+
+  /// Returns the elapsed time in milliseconds, rounded to the nearest
+  /// millisecond.
+  ULONGLONG GetElapsedMs() const;
+
+  /// Returns the elapsed time in seconds, rounded to the nearest second.
+  ULONGLONG GetElapsedSec() const;
+
+  ULONGLONG start_ticks() const { return start_ticks_; }
+
+  /// Returns timer frequency from cache, should be less
+  /// overhead than ::QueryPerformanceFrequency
+  static ULONGLONG GetTimerFrequency();
+  /// Returns current ticks
+  static ULONGLONG GetCurrentTicks();
+
+ private:
+  static void CollectPerfFreq();
+
+  /// Captured start time
+  ULONGLONG start_ticks_;
+
+  /// Captured performance counter frequency
+  static bool perf_freq_collected_;
+  static ULONGLONG perf_freq_;
+};
+
+inline HighresTimer::HighresTimer() {
+  Start();
+}
+
+inline void HighresTimer::Start() {
+  start_ticks_ = GetCurrentTicks();
+}
+
+inline ULONGLONG HighresTimer::GetTimerFrequency() {
+  if (!perf_freq_collected_)
+    CollectPerfFreq();
+  return perf_freq_;
+}
+
+inline ULONGLONG HighresTimer::GetCurrentTicks() {
+  LARGE_INTEGER ticks;
+  ::QueryPerformanceCounter(&ticks);
+  return ticks.QuadPart;
+}
+
+inline ULONGLONG HighresTimer::GetElapsedTicks() const {
+  return start_ticks_ - GetCurrentTicks();
+}
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_HIGHRES_TIMER_WIN32_H__
diff --git a/common/highres_timer_unittest.cc b/common/highres_timer_unittest.cc
new file mode 100644
index 0000000..09ad671
--- /dev/null
+++ b/common/highres_timer_unittest.cc
@@ -0,0 +1,68 @@
+// 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/lang_enc.h b/common/lang_enc.h
new file mode 100644
index 0000000..e6df865
--- /dev/null
+++ b/common/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_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/localization.cc b/common/localization.cc
new file mode 100644
index 0000000..4a14058
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..c710d72
--- /dev/null
+++ b/common/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_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
new file mode 100644
index 0000000..2fcc7f4
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..da29f1d
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..4a363f7
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..505e956
--- /dev/null
+++ b/common/logging.cc
@@ -0,0 +1,1323 @@
+// 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 <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/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;
+}
+
+// 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();
+}
+
+// Retrieve log directory
+CString Logging::GetLogDirectory() 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;
+}
+
+// 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 = GetLogDirectory();
+    if (path.IsEmpty() || log_file_name_.IsEmpty()) {
+      return false;
+    }
+    if (!File::Exists(path)) {
+      if (FAILED(CreateDir(path, NULL))) {
+        return false;
+      }
+    }
+    if (!::PathAppend(CStrBuf(path, MAX_PATH), log_file_name_)) {
+      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
new file mode 100644
index 0000000..3a7dadb
--- /dev/null
+++ b/common/logging.h
@@ -0,0 +1,496 @@
+// 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 log directory.
+  CString GetLogDirectory() 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
new file mode 100644
index 0000000..a74fd8b
--- /dev/null
+++ b/common/logging/build.scons
@@ -0,0 +1,38 @@
+#!/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
new file mode 100644
index 0000000..2101baa
--- /dev/null
+++ b/common/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/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/logging.h b/common/logging/logging.h
new file mode 100644
index 0000000..4008561
--- /dev/null
+++ b/common/logging/logging.h
@@ -0,0 +1,499 @@
+// 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_LOGGING_LOGGING_H__
+#define OMAHA_COMMON_LOGGING_LOGGING_H__
+
+#include <string>
+#include <cstring>
+#include <strstream>
+#include <tchar.h>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+// This file provides logging facility for Windows client apps.
+//
+// Optional message capabilities
+// -----------------------------
+// Assertion failed messages and fatal errors are displayed in a dialog box
+// before the application exits. However, running this UI creates a message
+// loop, which causes application messages to be processed and potentially
+// dispatched to existing application windows. Since the application is in a
+// bad state when this assertion dialog is displayed, these messages may not
+// get processed and hang the dialog, or the application might go crazy.
+//
+// Therefore, it can be beneficial to display the error dialog in a separate
+// process from the main application. When the logging system needs to display
+// a fatal error dialog box, it will look for a program called
+// "DebugMessage.exe" in the same directory as the application executable. It
+// will run this application with the message as the command line, and will
+// not include the name of the application as is traditional for easier
+// parsing.
+//
+// The code for DebugMessage.exe is only one line. In WinMain, do:
+//   MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0);
+//
+// If DebugMessage.exe is not found, the logging code will use a normal
+// MessageBox, potentially causing the problems discussed above.
+
+
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging.  The way to log things is to stream
+// things to LOG(<a particular severity level>).  E.g.,
+//
+//   LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+//   LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The above will cause log messages to be output on the 1st, 11th, 21st, ...
+// times it is executed.  Note that the special COUNTER value is used to
+// identify which repetition is happening.
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+//   DLOG(INFO) << "Found cookies";
+//
+//   DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles.  LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+//   LOG_ASSERT(assertion);
+//   DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
+//
+// There is also the special severity of DFATAL, which logs FATAL in
+// debug mode, ERROR in normal mode.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+
+namespace logging {
+
+// Where to record logging output? A flat file and/or system debug log via
+// OutputDebugString. Defaults to LOG_ONLY_TO_FILE.
+enum LoggingDestination { LOG_ONLY_TO_FILE, 
+                          LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+                          LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG };
+
+// Indicates that the log file should be locked when being written to.
+// Often, there is no locking, which is fine for a single threaded program.
+// If logging is being done from multiple threads or there can be more than
+// one process doing the logging, the file should be locked during writes to
+// make each log outut atomic. Other writers will block.
+//
+// All processes writing to the log file must have their locking set for it to
+// work properly. Defaults to DONT_LOCK_LOG_FILE.
+enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
+
+// On startup, should we delete or append to an existing log file (if any)?
+// Defaults to APPEND_TO_OLD_LOG_FILE.
+enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
+
+// Sets the log file name and other global logging state. Calling this function
+// is recommended, and is normally done at the beginning of application init.
+// If you don't call it, all the flags will be initialized to their default
+// values, and there is a race condition that may leak a critical section
+// object if two threads try to do the first log at the same time.
+// See the definition of the enums above for descriptions and default values.
+//
+// The default log file is initialized to "debug.log" in the application
+// directory. You probably don't want this, especially since the program
+// directory may not be writable on an enduser's system.
+void InitLogging(const TCHAR* log_file, LoggingDestination logging_dest,
+                 LogLockingState lock_log, OldFileDeletionState delete_old);
+
+// Sets the log level. Anything at or above this level will be written to the
+// log file/displayed to the user (if applicable). Anything below this level
+// will be silently ignored. The log level defaults to 0 (everything is logged)
+// if this function is not called.
+void SetMinLogLevel(int level);
+
+// Sets the common items you want to be prepended to each log message.
+// process and thread IDs default to off, the timestamp defaults to on.
+// If this function is not called, logging defaults to writing the timestamp
+// only.
+void SetLogItems(bool enable_process_id, bool enable_thread_id,
+                 bool enable_timestamp, bool enable_tickcount);
+
+// Sets the Log Assert Handler that will be used to notify of check failures.
+// The default handler shows a dialog box, however clients can use this 
+// function to override with their own handling (e.g. a silent one for Unit
+// Tests)
+typedef void (*LogAssertHandlerFunction)(const std::string& str);
+void SetLogAssertHandler(LogAssertHandlerFunction handler);
+
+typedef int LogSeverity;
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
+
+// LOG_DFATAL_LEVEL is LOG_FATAL in debug mode, ERROR in normal mode
+#ifdef NDEBUG
+const LogSeverity LOG_DFATAL_LEVEL = LOG_ERROR;
+#else
+const LogSeverity LOG_DFATAL_LEVEL = LOG_FATAL;
+#endif
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_INFO \
+  logging::LogMessage(__FILE__, __LINE__)
+#define COMPACT_GOOGLE_LOG_WARNING \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_WARNING)
+#define COMPACT_GOOGLE_LOG_ERROR \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR)
+#define COMPACT_GOOGLE_LOG_FATAL \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_FATAL)
+#define COMPACT_GOOGLE_LOG_DFATAL \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_DFATAL_LEVEL)
+
+// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
+// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
+// to keep using this syntax, we define this macro to do the same thing
+// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
+// the Windows SDK does for consistency.
+#define ERROR 0
+#define COMPACT_GOOGLE_LOG_0 \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO.  There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+
+#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
+#define SYSLOG(severity) LOG(severity)
+
+#define LOG_IF(severity, condition) \
+  !(condition) ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+#define SYSLOG_IF(severity, condition) LOG_IF(severity, condition)
+
+#define LOG_ASSERT(condition)  \
+  LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+#define SYSLOG_ASSERT(condition) \
+  SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+
+// CHECK dies with a fatal error if condition is not true.  It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode.
+#define CHECK(condition) \
+  LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "
+
+// A container for a string pointer which can be evaluated to a bool -
+// true iff the pointer is NULL.
+struct CheckOpString {
+  CheckOpString(std::string* str) : str_(str) { }
+  // No destructor: if str_ is non-NULL, we're about to LOG(FATAL),
+  // so there's no point in cleaning up str_.
+  operator bool() const { return str_ != NULL; }
+  std::string* str_;
+};
+
+// Build the error message string.  This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline.
+template<class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+  std::ostrstream ss;
+  ss << names << " (" << v1 << " vs. " << v2 << ")";
+  return new std::string(ss.str(), ss.pcount());
+}
+
+extern std::string* MakeCheckOpStringIntInt(int v1, int v2, const char* names);
+
+template<int, int>
+std::string* MakeCheckOpString(const int& v1, const int& v2, const char* names) {
+  return MakeCheckOpStringIntInt(v1, v2, names);
+}
+
+// Plus some debug-logging macros that get compiled to nothing for production
+//
+// DEBUG_MODE is for uses like
+//   if (DEBUG_MODE) foo.CheckThatFoo();
+// instead of
+//   #ifndef NDEBUG
+//     foo.CheckThatFoo();
+//   #endif
+
+#ifndef NDEBUG
+
+#define DLOG(severity) LOG(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+
+// debug-only checking.  not executed in NDEBUG mode.
+enum { DEBUG_MODE = 1 };
+#define DCHECK(condition) \
+  LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "
+
+// Helper functions for DCHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+#define DEFINE_DCHECK_OP_IMPL(name, op) \
+  template <class t1, class t2> \
+  inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+                                        const char* names) { \
+    if (v1 op v2) return NULL; \
+    else return MakeCheckOpString(v1, v2, names); \
+  } \
+  inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+    if (v1 op v2) return NULL; \
+    else return MakeCheckOpString(v1, v2, names); \
+  }
+DEFINE_DCHECK_OP_IMPL(EQ, ==)
+DEFINE_DCHECK_OP_IMPL(NE, !=)
+DEFINE_DCHECK_OP_IMPL(LE, <=)
+DEFINE_DCHECK_OP_IMPL(LT, < )
+DEFINE_DCHECK_OP_IMPL(GE, >=)
+DEFINE_DCHECK_OP_IMPL(GT, > )
+#undef DEFINE_DCHECK_OP_IMPL
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use CHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2)  \
+  while (logging::CheckOpString _result = \
+         logging::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
+    logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+// Equality/Inequality checks - compare two values, and log a LOG_FATAL message
+// including the two values when the result is not as expected.  The values
+// must have operator<<(ostream, ...) defined.
+//
+// You may append to the error message like so:
+//   CHECK_NE(1, 2) << ": The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here.  In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+//   CHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These don't compile correctly if one of the arguments is a pointer
+// and the other is NULL. To work around this, simply static_cast NULL to the
+// type of the desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2)
+#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
+
+// Helper functions for string comparisons.
+// To avoid bloat, the definitions are in logging.cc.
+#define DECLARE_DCHECK_STROP_IMPL(func, expected) \
+  std::string* Check##func##expected##Impl(const char* s1, \
+                                           const char* s2, \
+                                           const char* names);
+DECLARE_DCHECK_STROP_IMPL(strcmp, true)
+DECLARE_DCHECK_STROP_IMPL(strcmp, false)
+DECLARE_DCHECK_STROP_IMPL(_stricmp, true)
+DECLARE_DCHECK_STROP_IMPL(_stricmp, false)
+#undef DECLARE_DCHECK_STROP_IMPL
+
+// Helper macro for string comparisons.
+// Don't use this macro directly in your code, use CHECK_STREQ et al below.
+#define DCHECK_STROP(func, op, expected, s1, s2) \
+  while (CheckOpString _result = \
+      logging::Check##func##expected##Impl((s1), (s2), \
+                                           #s1 " " #op " " #s2)) \
+    LOG(FATAL) << *_result.str_
+
+// String (char*) equality/inequality checks.
+// CASE versions are case-insensitive.
+//
+// Note that "s1" and "s2" may be temporary strings which are destroyed
+// by the compiler at the end of the current "full expression"
+// (e.g. DCHECK_STREQ(Foo().c_str(), Bar().c_str())).
+
+#define DCHECK_STREQ(s1, s2) DCHECK_STROP(strcmp, ==, true, s1, s2)
+#define DCHECK_STRNE(s1, s2) DCHECK_STROP(strcmp, !=, false, s1, s2)
+#define DCHECK_STRCASEEQ(s1, s2) DCHECK_STROP(_stricmp, ==, true, s1, s2)
+#define DCHECK_STRCASENE(s1, s2) DCHECK_STROP(_stricmp, !=, false, s1, s2)
+
+#define DCHECK_INDEX(I,A) DCHECK(I < (sizeof(A)/sizeof(A[0])))
+#define DCHECK_BOUND(B,A) DCHECK(B <= (sizeof(A)/sizeof(A[0])))
+
+#else  // NDEBUG
+
+#define DLOG(severity) \
+  true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+
+#define DLOG_IF(severity, condition) \
+  true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+
+#define DLOG_ASSERT(condition) \
+  true ? (void) 0 : LOG_ASSERT(condition)
+
+enum { DEBUG_MODE = 0 };
+
+// This macro can be followed by a sequence of stream parameters in
+// non-debug mode. The DCHECK and friends macros use this so that
+// the expanded expression DCHECK(foo) << "asdf" is still syntactically
+// valid, even though the expression will get optimized away.
+#define NDEBUG_EAT_STREAM_PARAMETERS \
+  logging::LogMessage(__FILE__, __LINE__).stream()
+
+#define DCHECK(condition) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_EQ(val1, val2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_NE(val1, val2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_LE(val1, val2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_LT(val1, val2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_GE(val1, val2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_GT(val1, val2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STREQ(str1, str2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRCASEEQ(str1, str2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRNE(str1, str2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRCASENE(str1, str2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#endif  // NDEBUG
+
+#define NOTREACHED() DCHECK(false)
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message.  You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though.  You should use the LOG() macro (and variants thereof)
+// above.
+class LogMessage {
+ public:
+  LogMessage(const char* file, int line, LogSeverity severity, int ctr);
+
+  // Two special constructors that generate reduced amounts of code at
+  // LOG call sites for common cases.
+  //
+  // Used for LOG(INFO): Implied are:
+  // severity = LOG_INFO, ctr = 0
+  //
+  // Using this constructor instead of the more complex constructor above
+  // saves a couple of bytes per call site.
+  LogMessage(const char* file, int line);
+
+  // Used for LOG(severity) where severity != INFO.  Implied
+  // are: ctr = 0
+  //
+  // Using this constructor instead of the more complex constructor above
+  // saves a couple of bytes per call site.
+  LogMessage(const char* file, int line, LogSeverity severity);
+
+  // A special constructor used for check failures.
+  // Implied severity = LOG_FATAL
+  LogMessage(const char* file, int line, const CheckOpString& result);
+
+  ~LogMessage();
+
+  std::ostream& stream() { return stream_; }
+
+ private:
+  void Init(const char* file, int line);
+
+  LogSeverity severity_;
+  std::ostrstream stream_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(LogMessage);
+};
+
+// A non-macro interface to the log facility; (useful
+// when the logging level is not a compile-time constant).
+inline void LogAtLevel(int const log_level, std::string const &msg) {
+  LogMessage(__FILE__, __LINE__, log_level).stream() << msg;
+}
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros.  This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+  LogMessageVoidify() { }
+  // This has to be an operator with a precedence lower than << but
+  // higher than ?:
+  void operator&(std::ostream&) { }
+};
+
+// Closes the log file explicitly if open.
+// NOTE: Since the log file is opened as necessary by the action of logging
+//       statements, there's no guarantee that it will stay closed
+//       after this call.
+void CloseLogFile();
+
+} // namespace Logging
+
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these operators.
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+  return out << wstr.c_str();
+}
+
+#endif  // OMAHA_COMMON_LOGGING_LOGGING_H__
diff --git a/common/logging_unittest.cc b/common/logging_unittest.cc
new file mode 100644
index 0000000..4693a82
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..1477361
--- /dev/null
+++ b/common/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 "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.h b/common/md5.h
new file mode 100644
index 0000000..a688c3d
--- /dev/null
+++ b/common/md5.h
@@ -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.
+// ========================================================================
+
+/* MD5.H - header file for MD5C.C
+*/
+
+/* 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.
+*/
+
+/* GLOBAL.H - RSAREF types and constants
+*/
+
+#ifndef TR_COMMON_MD5_H_
+#define TR_COMMON_MD5_H_
+
+#include "base/basictypes.h"
+
+namespace omaha {
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* MD5 context */
+typedef struct {
+  uint32 state[4];                                  /* state (ABCD) */
+  uint32 count[2];        /* number of bits, modulo 2^64 (lsb first) */
+  unsigned char buffer[64];                         /* input buffer */
+} MD5_CTX;
+
+void MD5Init (MD5_CTX *);
+void MD5Update (MD5_CTX *, unsigned char *, unsigned int);
+void MD5Final (unsigned char [16], MD5_CTX *);
+
+}  // namespace omaha
+
+#endif  // TR_COMMON_MD5_H_
diff --git a/common/md5_unittest.cc b/common/md5_unittest.cc
new file mode 100644
index 0000000..ac4ff42
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..2a34a4c
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..9c4c61e
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..fa409b3
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..18c04e4
--- /dev/null
+++ b/common/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_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
new file mode 100644
index 0000000..2e7bf11
--- /dev/null
+++ b/common/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/common/object_factory.h"
+#include "omaha/testing/unit_test.h"
+
+// TODO(omaha): write unit tests
diff --git a/common/omaha_version.cc b/common/omaha_version.cc
new file mode 100644
index 0000000..05b8611
--- /dev/null
+++ b/common/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/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.h b/common/omaha_version.h
new file mode 100644
index 0000000..76f9f06
--- /dev/null
+++ b/common/omaha_version.h
@@ -0,0 +1,43 @@
+// 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_OMAHA_VERSION_H__
+#define OMAHA_COMMON_OMAHA_VERSION_H__
+
+#include <windows.h>
+#include <tchar.h>
+
+namespace omaha {
+
+// Overloading on pointer types and integral types is not possible in this
+// case and generally speaking not a good idea anyway. It leads to either
+// compile time ambiguities or surprising results at runtime, such as
+// which overload is being called if foobar(NULL).
+
+// Initializes the version variables from the version resource of the module.
+void InitializeVersionFromModule(HINSTANCE instance);
+
+// Initializes the version variables from a ULONGLONG version.
+void InitializeVersion(ULONGLONG version);
+
+// Returns the version string as "major.minor.build.patch".
+const TCHAR* GetVersionString();
+
+// Returns the version string as a ULONGLONG.
+ULONGLONG GetVersion();
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_OMAHA_VERSION_H__
diff --git a/common/omaha_version_unittest.cc b/common/omaha_version_unittest.cc
new file mode 100644
index 0000000..ec6e4ad
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..2767254
--- /dev/null
+++ b/common/path.cc
@@ -0,0 +1,448 @@
+// 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));
+  ASSERT1(starts_with_quote == ends_with_quote);
+  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
new file mode 100644
index 0000000..f3f288b
--- /dev/null
+++ b/common/path.h
@@ -0,0 +1,87 @@
+// 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
new file mode 100644
index 0000000..4769baa
--- /dev/null
+++ b/common/path_unittest.cc
@@ -0,0 +1,413 @@
+// 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
new file mode 100644
index 0000000..1e3b1e5
--- /dev/null
+++ b/common/pe_utils.cc
@@ -0,0 +1,215 @@
+// 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.h b/common/pe_utils.h
new file mode 100644
index 0000000..cc969c6
--- /dev/null
+++ b/common/pe_utils.h
@@ -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.
+// ========================================================================
+//
+// Utility functions related to PE files (executables)
+
+#ifndef OMAHA_COMMON_PE_UTILS_H_
+#define OMAHA_COMMON_PE_UTILS_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+HRESULT SetPEChecksum(const TCHAR *filename, uint32 checksum);
+
+HRESULT SetPEChecksumToBuffer(unsigned char *buffer,
+                              size_t size, uint32 checksum);
+
+HRESULT GetPEChecksum(const TCHAR *filename, uint32 * checksum);
+
+HRESULT GetPEChecksumFromBuffer(const unsigned char *buffer,
+                                size_t size,
+                                uint32 *checksum);
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_PE_UTILS_H_
diff --git a/common/pe_utils_unittest.cc b/common/pe_utils_unittest.cc
new file mode 100644
index 0000000..4a14dca
--- /dev/null
+++ b/common/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/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/popup_menu.cc b/common/popup_menu.cc
new file mode 100644
index 0000000..7b9c00a
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..74c40c3
--- /dev/null
+++ b/common/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/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/preprocessor_fun.h b/common/preprocessor_fun.h
new file mode 100644
index 0000000..c2f6c12
--- /dev/null
+++ b/common/preprocessor_fun.h
@@ -0,0 +1,36 @@
+// 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.
+// ========================================================================
+
+// PP_STRINGIZE - expands arguments before stringizing
+// PP_STRINGIZE(PP_CAT(a,b)) => "ab"
+
+#define PP_STRINGIZE(text)   PP_STRINGIZE_A((text))
+#define PP_STRINGIZE_A(arg)  PP_STRINGIZE_B ## (arg)
+#define PP_STRINGIZE_B(arg)  PP_STRINGIZE_I ## arg
+#define PP_STRINGIZE_I(text) #text
+
+// T_PP_STRINGIZE - expands arguments before stringizing
+// T_PP_STRINGIZE(PP_CAT(a,b)) => _T("ab")
+
+#define T_PP_STRINGIZE(text)   T_PP_STRINGIZE_A((text))
+#define T_PP_STRINGIZE_A(arg)  T_PP_STRINGIZE_B ## (arg)
+#define T_PP_STRINGIZE_B(arg)  T_PP_STRINGIZE_I ## arg
+#define T_PP_STRINGIZE_I(text) _T(#text)
+
+// PP_CAT - concatenates arguments after they have been expanded
+// PP_CAT(x, PP_CAT(y,z)) => xyz
+#define PP_CAT(a,b)   PP_CAT_I(a,b)
+#define PP_CAT_I(a,b) PP_CAT_J(a##b)
+#define PP_CAT_J(arg) arg
diff --git a/common/proc_utils.cc b/common/proc_utils.cc
new file mode 100644
index 0000000..f362c33
--- /dev/null
+++ b/common/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/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.h b/common/proc_utils.h
new file mode 100644
index 0000000..4cc78c6
--- /dev/null
+++ b/common/proc_utils.h
@@ -0,0 +1,148 @@
+// 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_PROC_UTILS_H_
+#define OMAHA_COMMON_PROC_UTILS_H_
+
+#include <windows.h>
+#include <tlhelp32.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// Moved here from installation directory. Generic enought to be in common.
+class ProcessTerminator {
+ public:
+
+  // constants for specifying which methods to attempt when killing a process
+  static const int KILL_METHOD_1_WINDOW_MESSAGE = 0x01;
+  static const int KILL_METHOD_2_THREAD_MESSAGE = 0x02;
+  static const int KILL_METHOD_4_TERMINATE_PROCESS = 0x08;
+  static const int INVALID_SESSION_ID = 0xFFFF;
+
+  // Creates the object given the process name to kill.
+  explicit ProcessTerminator(const CString& process_name);
+  ProcessTerminator(const CString& process_name, const CString& user_sid);
+  ProcessTerminator(const CString& process_name,
+                    const CString& user_sid,
+                    int session_id);
+
+  // Performs necessary cleanup.
+  ~ProcessTerminator();
+
+  // Go through process list try to find the required one to kill,
+  // trying three methods to kill, from easiest and cleanest to a
+  // harsh one.  S_OK if no process by the right name was found, or if it was
+  // found and was killed.  E_FAIL otherwise.  was_found returns true if
+  // process was found. Kills all instances of a process.
+  HRESULT KillTheProcess(uint32 timeout_msec,
+                         bool* was_found,
+                         uint32 method_mask,
+                         bool flash_window);
+
+  // Wait for all instances of the process to die.
+  HRESULT WaitForAllToDie(uint32 timeout_msec);
+
+  // Finds all process ids for the process of a given name.
+  bool FindProcessInstances();
+
+ private:
+
+  // 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
+  bool PrepareToKill(uint32 method_mask);
+
+  // Wait for process instances to die for timeout_msec
+  // return true if all are dead and false if timed out.
+  bool WaitForProcessInstancesToDie(uint32 timeout_msec) const;
+
+  // Will close all currently opened handles.
+  void CloseAllHandles();
+
+
+  //
+  // Killing via messages to window
+  //
+  // Function which meet win32 requirements for callback
+  // function passed into EnumWindows function.
+  BOOL static CALLBACK EnumAllWindowsProc(HWND hwnd, LPARAM lparam);
+
+  // Will return true if it succeeds in finding a window for the process
+  // to be killed, otherwise false.  If there are such top-level windows
+  // then returns an array of window handles.
+  bool FindProcessWindows();
+
+  // Will try to kill the process via posting windows messages
+  // returns true on success otherwise false.
+  // Timeout is maximum time to wait for WM_CLOSE to work before going to
+  // next method.
+  bool KillProcessViaWndMessages(uint32 timeout_msec);
+
+  //
+  // Killing via messages to thread
+  //
+  // Try to find the threads than run in
+  // the process in question.
+  bool FindProcessThreads(std::vector<uint32>* thread_ids);
+
+  // Will try to kill the process via posing thread messages
+  // returns true on success otherwise false.
+  // Timeout is maximum time to wait for message to work before going to
+  // next method.
+  bool KillProcessViaThreadMessages(uint32 timeout_msec);
+
+  // The last and crude method to kill the process.
+  // Calls TerminateProcess function.
+  bool KillProcessViaTerminate(uint32 timeout_msec);
+
+  // Private member variables:
+  CString process_name_;
+  // One process can have several instances
+  // running. This array will keep handles to all
+  // instances of the process.
+  std::vector<HANDLE> process_handles_;
+  // Array of process ids which correspond to different
+  // instances of the same process.
+  std::vector<uint32>  process_ids_;
+  // Function PrepareToKill can call itself
+  // recursively under some conditions.
+  // We need to stop the recursion at some point.
+  // This is the purpose of this member.
+  int recursion_level_;
+  // Array of window handles.
+  std::vector<HWND> window_handles_;
+  // The sid of the user whose process needs to be terminated.
+  CString user_sid_;
+  // True if the window flashes on shut down.
+  bool flash_window_;
+  // The session to search the processes in.
+  int session_id_;
+
+  // Disable copy constructor and assignment operator.
+  DISALLOW_EVIL_CONSTRUCTORS(ProcessTerminator);
+};
+
+// Application calling this function will be shut down
+// by the system without displaying message boxes if the application
+// fails to shutdown itself properly as a result of processing
+// WM_QUERYENDSESSION.
+HRESULT SetProcessSilentShutdown();
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_PROC_UTILS_H_
diff --git a/common/proc_utils_unittest.cc b/common/proc_utils_unittest.cc
new file mode 100644
index 0000000..3b5c618
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..2680be6
--- /dev/null
+++ b/common/process.cc
@@ -0,0 +1,1631 @@
+// 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
new file mode 100644
index 0000000..4cabdf8
--- /dev/null
+++ b/common/process.h
@@ -0,0 +1,306 @@
+// 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
new file mode 100644
index 0000000..ae2a176
--- /dev/null
+++ b/common/process_unittest.cc
@@ -0,0 +1,217 @@
+// 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
new file mode 100644
index 0000000..e07f92b
--- /dev/null
+++ b/common/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/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/processor_type.h b/common/processor_type.h
new file mode 100644
index 0000000..cedf778
--- /dev/null
+++ b/common/processor_type.h
@@ -0,0 +1,29 @@
+// 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 declarations
+//
+
+#ifndef OMAHA_COMMON_PROCESSOR_TYPE_H_
+#define OMAHA_COMMON_PROCESSOR_TYPE_H_
+
+namespace omaha {
+
+bool IsIntelProcessor();
+CString GetProcessorType();
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_PROCESSOR_TYPE_H_
diff --git a/common/queue_timer.cc b/common/queue_timer.cc
new file mode 100644
index 0000000..5bae3d6
--- /dev/null
+++ b/common/queue_timer.cc
@@ -0,0 +1,183 @@
+// 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.h b/common/queue_timer.h
new file mode 100644
index 0000000..10d9182
--- /dev/null
+++ b/common/queue_timer.h
@@ -0,0 +1,83 @@
+// 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.
+// ========================================================================
+//
+// QueueTimer is a wrapper for the kernel queue timer.
+//
+// There are two ways to use the QueueTimer:
+// - alarm, where the timer goes off only once.
+// - periodic timer.
+// When working as an alarm, the timer must be restarted after it fired. There
+// is no need to destroy the whole object.
+// When working with a periodic timer, the timer can only be started once.
+// Alarm timers fire only once, so they will have to be restarted every time.
+// It is easy to deadlock when working with timers. As a general rule, never
+// destroy a QueueTimer from its callback.
+
+#ifndef OMAHA_COMMON_QUEUE_TIMER_H__
+#define OMAHA_COMMON_QUEUE_TIMER_H__
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class QueueTimer {
+  public:
+    typedef void (*Callback)(QueueTimer* timer);
+
+    QueueTimer(HANDLE timer_queue,  // Caller provided timer queue.
+               Callback callback,   // Callback to call when the timer fires.
+               void* ctx);          // Caller provided context.
+
+    // The destructor waits for the pending callbacks to finish since we do not
+    // want the callback to fire after the C++ object was destroyed.
+    ~QueueTimer();
+
+    // Starts a timer. The time is in milliseconds.
+    HRESULT Start(int due_time, int period, uint32 flags);
+
+    void* ctx() const { return ctx_; }
+
+    int due_time() const { return due_time_; }
+
+    int period() const { return period_; }
+
+    uint32 flags() const { return flags_; }
+
+  private:
+    static void _stdcall TimerCallback(void* param, BOOLEAN timer_or_wait);
+
+    HRESULT DoStart(int due_time, int period, uint32 flags);
+    void DoCallback();
+
+    CRITICAL_SECTION cs_;         // Serializes access to shared state.
+    CRITICAL_SECTION dtor_cs_;    // Serializes the destruction of the object.
+    DWORD callback_tid_;          // The thread id of the callback, if any.
+    void* ctx_;
+    int due_time_;
+    int period_;
+    uint32 flags_;
+    HANDLE timer_handle_;
+    HANDLE timer_queue_;
+    Callback callback_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(QueueTimer);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_QUEUE_TIMER_H__
+
diff --git a/common/queue_timer_unittest.cc b/common/queue_timer_unittest.cc
new file mode 100644
index 0000000..65a1c8a
--- /dev/null
+++ b/common/queue_timer_unittest.cc
@@ -0,0 +1,175 @@
+// 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 + 50, 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 + 50, 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
new file mode 100644
index 0000000..c9e10a3
--- /dev/null
+++ b/common/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/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.h b/common/reactor.h
new file mode 100644
index 0000000..7cad78e
--- /dev/null
+++ b/common/reactor.h
@@ -0,0 +1,92 @@
+// 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.
+// ========================================================================
+//
+// reactor.{h, cc} implements the reactor design pattern.
+//
+// For each signaled handle the reactor calls back the event handler only once.
+// To get further notifications, the handle must be registered again with the
+// reactor. This may sound like a lot of work but this is the only way to
+// guarantee that the event handler is only called once.
+// UnregisterHandle can't be called when handling a callback or it results in
+// a deadlock. When handling a callback, the only possible operation is
+// registering back a handle using Register.
+
+#ifndef OMAHA_COMMON_REACTOR_H__
+#define OMAHA_COMMON_REACTOR_H__
+
+#include <windows.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class EventHandler;
+
+class Reactor {
+ public:
+  Reactor();
+  ~Reactor();
+
+  // Starts demultiplexing and dispatching events.
+  HRESULT HandleEvents();
+
+  // Registers an event handler for a handle. The reactor does not own the
+  // handle. Registering the same handle twice results in undefined behavior.
+  // The flags parameter can be one of the WT* thread pool values or 0 for
+  // a reasonable default.
+  HRESULT RegisterHandle(HANDLE handle,
+                         EventHandler* event_handler,
+                         uint32 flags);
+
+  // Registers the handle again. This method can be called from the callback.
+  HRESULT RegisterHandle(HANDLE handle);
+
+  // Unregisters the handle. The method blocks and waits for any callback to
+  // complete if an event dispatching is in progress.
+  HRESULT UnregisterHandle(HANDLE handle);
+
+ private:
+  struct RegistrationState {
+    RegistrationState()
+        : reactor(NULL),
+          event_handler(NULL),
+          handle(NULL),
+          wait_handle(NULL),
+          flags(0) {}
+
+    Reactor*      reactor;
+    EventHandler* event_handler;
+    HANDLE        handle;
+    HANDLE        wait_handle;
+    uint32        flags;
+  };
+
+  static void __stdcall Callback(void* param, BOOLEAN timer_or_wait);
+  void DoCallback(RegistrationState* registration_state);
+
+  HRESULT DoRegisterHandle(HANDLE handle);
+
+  // Releases the ownership of the registration state corresponding to a handle.
+  RegistrationState* ReleaseHandlerState(HANDLE handle);
+
+  CRITICAL_SECTION cs_;
+  std::vector<RegistrationState*> handlers_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Reactor);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_REACTOR_H__
diff --git a/common/reactor_unittest.cc b/common/reactor_unittest.cc
new file mode 100644
index 0000000..bacb397
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..ddfc61b
--- /dev/null
+++ b/common/reg_key.cc
@@ -0,0 +1,1265 @@
+// 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 != 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
new file mode 100644
index 0000000..fbf2dcf
--- /dev/null
+++ b/common/reg_key.h
@@ -0,0 +1,784 @@
+// 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
new file mode 100644
index 0000000..ec8032d
--- /dev/null
+++ b/common/reg_key_unittest.cc
@@ -0,0 +1,777 @@
+// 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 kStRkey1          _T("HKCU\\Software\\Google\\Update\\TEST")
+#define kRkey1            _T("Software\\Google\\Update\\TEST")
+#define kStRkey2          _T("HKCU\\Software\\Google\\Update\\TEST2")
+#define kStRkey3          _T("HKCU\\Software\\Google\\Update\\TEST3")
+#define kRkey1SubkeyName  _T("subkey_test")
+#define kRkey1Subkey      kRkey1 _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 {
+ public:
+  static const HKEY GetHKey(const RegKey& reg) {
+    return reg.h_key_;
+  }
+
+  static CString GetParentKeyInfo(CString* key_name) {
+    return RegKey::GetParentKeyInfo(key_name);
+  }
+};
+
+// 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(kStRkey1);
+
+  // 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(kStRkey1);
+  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(kStRkey1);
+  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(RegKeyTest, CreateKeys) {
+  RegKey::DeleteKey(kStRkey1);
+  RegKey::DeleteKey(kStRkey2);
+  RegKey::DeleteKey(kStRkey3);
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey2));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey3));
+
+  // 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));
+
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kStRkey1));
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kStRkey2));
+}
+
+TEST(RegKeyTest, CreateKey) {
+  RegKey::DeleteKey(kStRkey1);
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+
+  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
+
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kStRkey1));
+}
+
+TEST(RegKeyTest, 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());
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kStRkey1));
+}
+
+TEST(RegKeyTest, 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);
+
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kStRkey1));
+}
+
+TEST(RegKeyTest, 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);
+
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kStRkey1));
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kStRkey2));
+}
+
+}  // namespace omaha
+
diff --git a/common/regexp.cc b/common/regexp.cc
new file mode 100644
index 0000000..a79889e
--- /dev/null
+++ b/common/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 "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/regexp.h b/common/regexp.h
new file mode 100644
index 0000000..25781c1
--- /dev/null
+++ b/common/regexp.h
@@ -0,0 +1,103 @@
+// 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.
+// ========================================================================
+//
+// Implementors: There is only one function to implement -- DoMatchImpl
+// See below
+
+#ifndef OMAHA_COMMON_REGEXP_H__
+#define OMAHA_COMMON_REGEXP_H__
+
+#include <atlstr.h>
+
+namespace omaha {
+
+// Interface for regular expression matching.  Also corresponds to a
+// pre-compiled regular expression.  An "RE" object is safe for
+// concurrent use by multiple threads.
+class RE {
+ public:
+
+  // Matches "text" against "pattern".  If pointer arguments are
+  // supplied, copies matched sub-patterns into them. Use braces
+  // "{", "}" within the regexp to indicate a pattern to be copied.
+  //
+  // Returns true iff all of the following conditions are satisfied:
+  //   a. some substring of "text" matches "pattern"
+  //   b. The number of matched sub-patterns is >= number of supplied pointers
+  static bool PartialMatch(const TCHAR* text, const RE& re, // 3..16 args
+    CString * a0 = NULL,
+    CString * a1 = NULL,
+    CString * a2 = NULL,
+    CString * a3 = NULL,
+    CString * a4 = NULL,
+    CString * a5 = NULL,
+    CString * a6 = NULL,
+    CString * a7 = NULL,
+    CString * a8 = NULL,
+    CString * a9 = NULL,
+    CString * a10 = NULL,
+    CString * a11 = NULL,
+    CString * a12 = NULL,
+    CString * a13 = NULL,
+    CString * a14 = NULL,
+    CString * a15 = 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".
+  static bool FindAndConsume(const TCHAR** input, const RE& re,
+    CString * a0 = NULL,
+    CString * a1 = NULL,
+    CString * a2 = NULL,
+    CString * a3 = NULL,
+    CString * a4 = NULL,
+    CString * a5 = NULL,
+    CString * a6 = NULL,
+    CString * a7 = NULL,
+    CString * a8 = NULL,
+    CString * a9 = NULL,
+    CString * a10 = NULL,
+    CString * a11 = NULL,
+    CString * a12 = NULL,
+    CString * a13 = NULL,
+    CString * a14 = NULL,
+    CString * a15 = NULL);
+
+ protected:
+
+  // The behavior of this function is subject to how it's used
+  // in PartialMatch() and FindAndConsume() above. See the header
+  // description of those functions to understand how an implementation
+  // should behave.
+  // text is the text we're looking in
+  // args is where matches should be outputted
+  // n is the number of CStrings in args
+  // match_end is a pointer to the position in text that
+  // we ended matching on
+  // returns true if data was found, false otherwise
+  // Example:Suppose text = "google 1\nYahoo! 2\n ..." and the regexp
+  // is something like "{\w+} \d". If args has two CStrings (n=2),
+  // then args[0] = "google", arg[1] = "1" and match_end will point to the \n
+  // before "Yahoo!"
+  virtual bool DoMatchImpl(const TCHAR *text,
+    CString * args[],
+    int n,
+    const TCHAR ** match_end) const = 0;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_REGEXP_H__
diff --git a/common/registry_hive.cc b/common/registry_hive.cc
new file mode 100644
index 0000000..69458ed
--- /dev/null
+++ b/common/registry_hive.cc
@@ -0,0 +1,268 @@
+// 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_hive.h b/common/registry_hive.h
new file mode 100644
index 0000000..cf34680
--- /dev/null
+++ b/common/registry_hive.h
@@ -0,0 +1,74 @@
+// 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.
+// ========================================================================
+//
+// Other persons' registry hive manipulations.
+//
+#ifndef OMAHA_COMMON_REGISTRY_HIVE_H_
+#define OMAHA_COMMON_REGISTRY_HIVE_H_
+
+#include <windows.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+#define kProfileKeyFormat \
+    _T("HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%s")
+#define kProfilePathValue _T("ProfileImagePath")
+#define kHiveName         _T("Ntuser.dat")
+
+// !!! Warning !!!
+// You MUST unload hive, or this hive becomes unavailable until PC is restarted
+// This means the person cannot login.
+class RegistryHive {
+ public:
+  RegistryHive();
+  ~RegistryHive();
+
+  // 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.
+  HRESULT LoadHive(TCHAR const * sid, TCHAR const *name);
+  // 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 LoadHive(TCHAR const * sid);
+  // Unloads and saves loaded hive
+  HRESULT UnloadHive();
+  // Does it recursively. The name should be relative to HKEY_CURRENT_USER.
+  HRESULT DeleteUserKey(TCHAR const * key_name);
+  // Expands key name for hive:
+  // "Software\Google" => "HKEY_USERS\[hive_name_]\Software\Google"
+  void ExpandKeyName(CString * str);
+
+ private:
+  CString hive_name_;
+  HKEY hive_holding_key_;
+};
+
+// Function pointer
+// Return non-zero to abort the enumeration
+typedef int ProcessUserRegistryFunc(const TCHAR* user_sid,
+                                    const TCHAR* user_reg_key,
+                                    LONG_PTR param);
+
+// Enumerate all user registries
+void EnumerateAllUserRegistries(ProcessUserRegistryFunc* handler,
+                                LONG_PTR param);
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_REGISTRY_HIVE_H_
diff --git a/common/registry_monitor_manager.cc b/common/registry_monitor_manager.cc
new file mode 100644
index 0000000..43eb5f7
--- /dev/null
+++ b/common/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/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.h b/common/registry_monitor_manager.h
new file mode 100644
index 0000000..6cf9952
--- /dev/null
+++ b/common/registry_monitor_manager.h
@@ -0,0 +1,92 @@
+// 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 RegistryMonitor allows a caller to request monitoring
+// for registry value changes across multiple keys. It uses the Windows API
+// function RegNotifyChangeKeyValue to notify when any value in a key changes.
+
+#ifndef OMAHA_COMMON_REGISTRY_MONITOR_MANAGER_H_
+#define OMAHA_COMMON_REGISTRY_MONITOR_MANAGER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+namespace omaha {
+
+namespace detail {
+
+class RegistryMonitorImpl;
+
+}  // namespace detail
+
+// Called when a registry value changes. 'new_value_data' contains a
+// pointer to the string data for a string value or the value itself for a
+// DWORD value.
+enum RegistryChangeType {
+  REGISTRY_CHANGE_TYPE_CREATE = 0,
+  REGISTRY_CHANGE_TYPE_UPDATE,
+  REGISTRY_CHANGE_TYPE_DELETE,
+};
+typedef void (*RegistryValueChangeCallback)(const TCHAR* key_name,
+                                            const TCHAR* value_name,
+                                            RegistryChangeType change_type,
+                                            const void* new_value_data,
+                                            void* user_data);
+
+// Called when a registry key changes. Changes include subkeys being
+// created or deleted as well as value changes under that key but not under
+// the subkeys of the key.
+typedef void (*RegistryKeyChangeCallback)(const TCHAR* key_name,
+                                          void* user_data);
+
+class RegistryMonitor {
+ public:
+  RegistryMonitor();
+  ~RegistryMonitor();
+
+  HRESULT Initialize();
+
+  // Monitors a registry sub key for changes. Registering the same sub key
+  // overrides the previous registration.
+  HRESULT MonitorKey(HKEY root_key,
+                     const CString& sub_key,
+                     RegistryKeyChangeCallback callback,
+                     void* user_data);
+
+  // Adds a registry value to the list of values to monitor for changes.
+  // All values must be registered before starting monitoring. Registering
+  // the same value is allowed, although not particularly useful.
+  HRESULT MonitorValue(HKEY root_key,
+                       const CString& sub_key,
+                       const CString& value_name,
+                       int value_type,
+                       RegistryValueChangeCallback callback,
+                       void* user_data);
+
+  // Starts monitoring for changes.
+  HRESULT StartMonitoring();
+
+ private:
+  scoped_ptr<detail::RegistryMonitorImpl> impl_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(RegistryMonitor);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_REGISTRY_MONITOR_MANAGER_H_
+
diff --git a/common/registry_monitor_manager_unittest.cc b/common/registry_monitor_manager_unittest.cc
new file mode 100644
index 0000000..ef20634
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..dca1d63
--- /dev/null
+++ b/common/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/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.h b/common/registry_store.h
new file mode 100644
index 0000000..d7e55cb
--- /dev/null
+++ b/common/registry_store.h
@@ -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.
+// ========================================================================
+//
+// Declares class RegistryStore
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OMAHA_COMMON_REGISTRY_STORE_H__
+#define OMAHA_COMMON_REGISTRY_STORE_H__
+
+#include <wtypes.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class RegistryStore {
+ public:
+  // Constructor
+  RegistryStore() {}
+
+  // Destructor
+  ~RegistryStore() {}
+
+  // Open the store
+  bool Open(const TCHAR* key_path);
+
+  // Close the store
+  bool Close();
+
+  // Clear the store
+  bool Clear();
+
+  // Read a value from the store
+  bool Read(const TCHAR* name, std::vector<byte>* data) const;
+
+  // Write a value to the store
+  bool Write(const TCHAR* name, byte* data, int data_size);
+
+  // Check to see a named value exists in the store
+  bool Exists(const TCHAR* name);
+
+  // Remove a value from the store
+  bool Remove(const TCHAR* name);
+
+  // Get the number of values for this key
+  bool GetValueCount(uint32* value_count);
+
+  // Get the value name for the given value name index
+  bool GetValueNameAt(int index, CString* value_name);
+
+ private:
+  CString key_path_;      // Full path to the registry key
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_REGISTRY_STORE_H__
diff --git a/common/registry_store_unittest.cc b/common/registry_store_unittest.cc
new file mode 100644
index 0000000..c75de3a
--- /dev/null
+++ b/common/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/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/scope_guard.h b/common/scope_guard.h
new file mode 100644
index 0000000..326dc0c
--- /dev/null
+++ b/common/scope_guard.h
@@ -0,0 +1,337 @@
+//
+// 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_any.h b/common/scoped_any.h
new file mode 100644
index 0000000..d7735f8
--- /dev/null
+++ b/common/scoped_any.h
@@ -0,0 +1,36 @@
+// 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.
+// ========================================================================
+
+// smartany library uses a poor compile time assert. It is actually generating
+// runtime code to call the constructor of the assert class. Most likely to
+// improve performance, the library uses a static instance of the compile time
+// assert object. In theory the code is not thread-safe. In practice, since the
+// compile time assert is an empty class, there should be no problems. The long
+// term solution requires changing smartany to use a better compile time assert
+// which is completely evaluated at compile time and it has no effects
+// whatsoever at runtime. Short term, the code should include these wrappers
+// to silence the compiler warning.
+
+#ifndef OMAHA_COMMON_SCOPED_ANY__
+#define OMAHA_COMMON_SCOPED_ANY__
+
+#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_SCOPED_ANY__
+
diff --git a/common/scoped_current_directory.h b/common/scoped_current_directory.h
new file mode 100644
index 0000000..ecb2aba
--- /dev/null
+++ b/common/scoped_current_directory.h
@@ -0,0 +1,46 @@
+// 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_SCOPED_CURRENT_DIRECTORY_H_
+#define OMAHA_COMMON_SCOPED_CURRENT_DIRECTORY_H_
+
+namespace omaha {
+
+// Utility class to "scope" the setting of a current directory, and restore
+// to the previous current directory when leaving the scope.  Handles the
+// case where we don't actually have a new current directory to switch to
+// (thus the parameter is the empty string).
+
+class scoped_current_directory {
+  public:
+    explicit scoped_current_directory(const TCHAR* new_directory) {
+      *was_directory_ = _T('\0');
+      if (new_directory && *new_directory) {
+        ::GetCurrentDirectory(arraysize(was_directory_), was_directory_);
+        ::SetCurrentDirectory(new_directory);
+      }
+    }
+    ~scoped_current_directory() {
+      if (*was_directory_) {
+        ::SetCurrentDirectory(was_directory_);
+      }
+    }
+  private:
+    TCHAR was_directory_[MAX_PATH];
+};
+
+}  // namespace omaha
+
+#endif // OMAHA_COMMON_SCOPED_CURRENT_DIRECTORY_H_
diff --git a/common/scoped_impersonation.h b/common/scoped_impersonation.h
new file mode 100644
index 0000000..e7efec6
--- /dev/null
+++ b/common/scoped_impersonation.h
@@ -0,0 +1,63 @@
+// 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
new file mode 100644
index 0000000..cc2c7a2
--- /dev/null
+++ b/common/scoped_impersonation_unittest.cc
@@ -0,0 +1,41 @@
+// 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
new file mode 100644
index 0000000..aa0be1c
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..ad0914b
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..7414eb3
--- /dev/null
+++ b/common/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/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/aes.c b/common/security/aes.c
new file mode 100644
index 0000000..47cbcf8
--- /dev/null
+++ b/common/security/aes.c
@@ -0,0 +1,177 @@
+// 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.
+// ========================================================================
+//
+// Optimized for code size. Lacking decrypt functionality.
+// Currently 1042 bytes of code.
+
+#include "aes.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <inttypes.h>
+
+static const uint8_t sbox_e[256]= {
+    99  , 124 , 119 , 123 , 242 , 107 , 111 , 197
+  , 48  , 1   , 103 , 43  , 254 , 215 , 171 , 118
+  , 202 , 130 , 201 , 125 , 250 , 89  , 71  , 240
+  , 173 , 212 , 162 , 175 , 156 , 164 , 114 , 192
+  , 183 , 253 , 147 , 38  , 54  , 63  , 247 , 204
+  , 52  , 165 , 229 , 241 , 113 , 216 , 49  , 21
+  , 4   , 199 , 35  , 195 , 24  , 150 , 5   , 154
+  , 7   , 18  , 128 , 226 , 235 , 39  , 178 , 117
+  , 9   , 131 , 44  , 26  , 27  , 110 , 90  , 160
+  , 82  , 59  , 214 , 179 , 41  , 227 , 47  , 132
+  , 83  , 209 , 0   , 237 , 32  , 252 , 177 , 91
+  , 106 , 203 , 190 , 57  , 74  , 76  , 88  , 207
+  , 208 , 239 , 170 , 251 , 67  , 77  , 51  , 133
+  , 69  , 249 , 2   , 127 , 80  , 60  , 159 , 168
+  , 81  , 163 , 64  , 143 , 146 , 157 , 56  , 245
+  , 188 , 182 , 218 , 33  , 16  , 255 , 243 , 210
+  , 205 , 12  , 19  , 236 , 95  , 151 , 68  , 23
+  , 196 , 167 , 126 , 61  , 100 , 93  , 25  , 115
+  , 96  , 129 , 79  , 220 , 34  , 42  , 144 , 136
+  , 70  , 238 , 184 , 20  , 222 , 94  , 11  , 219
+  , 224 , 50  , 58  , 10  , 73  , 6   , 36  , 92
+  , 194 , 211 , 172 , 98  , 145 , 149 , 228 , 121
+  , 231 , 200 , 55  , 109 , 141 , 213 , 78  , 169
+  , 108 , 86  , 244 , 234 , 101 , 122 , 174 , 8
+  , 186 , 120 , 37  , 46  , 28  , 166 , 180 , 198
+  , 232 , 221 , 116 , 31  , 75  , 189 , 139 , 138
+  , 112 , 62  , 181 , 102 , 72  , 3   , 246 , 14
+  , 97  , 53  , 87  , 185 , 134 , 193 , 29  , 158
+  , 225 , 248 , 152 , 17  , 105 , 217 , 142 , 148
+  , 155 , 30  , 135 , 233 , 206 , 85  , 40  , 223
+  , 140 , 161 , 137 , 13  , 191 , 230 , 66  , 104
+  , 65  , 153 , 45  , 15  , 176 , 84  , 187 , 22
+  };
+
+static uint8_t xtime(int in) {
+  in <<= 1;
+  if( in&0x100 ) in ^= 0x11b;
+  return (uint8_t)in;
+  }
+
+static void expand_key(const uint8_t* key, uint32_t* expanded_key) {
+  int   nrounds;
+  union {
+    uint8_t b[16];
+    uint32_t w[4];
+    } W;
+  uint8_t xor = 1;
+
+  memcpy( &W, key, 16 );
+  memcpy( expanded_key, &W, 16 );
+
+  for( nrounds = 0; nrounds < 10; ++nrounds ) {
+
+      // update key schedule
+    W.b[0] ^= sbox_e[W.b[12+1]] ^ xor;
+    W.b[1] ^= sbox_e[W.b[12+2]];
+    W.b[2] ^= sbox_e[W.b[12+3]];
+    W.b[3] ^= sbox_e[W.b[12+0]];
+    W.w[1] ^= W.w[0];
+    W.w[2] ^= W.w[1];
+    W.w[3] ^= W.w[2];
+
+    xor = xtime( xor );
+
+    expanded_key += 4;
+    memcpy( expanded_key, &W, 16 );
+    }
+  }
+
+void AES_encrypt_block(const uint8_t* key, const uint8_t* in, uint8_t* out) {
+  int j, nrounds;
+  union {
+    uint8_t b[16];
+    uint32_t w[4];
+    } rd_state;
+  uint32_t expanded_key[11 * 4];
+  uint32_t* expkey = &expanded_key[0];
+
+  expand_key( key, expanded_key );
+
+  memcpy( &rd_state, in, 16 );
+
+    // xor with initial key
+  rd_state.w[0] ^= *expkey++;
+  rd_state.w[1] ^= *expkey++;
+  rd_state.w[2] ^= *expkey++;
+  rd_state.w[3] ^= *expkey++;
+
+  nrounds = 10;
+
+  do {
+    uint8_t tmp;
+
+      // bytesub && shiftrow
+
+      // 1st
+    rd_state.b[0] = sbox_e[rd_state.b[0]];
+    rd_state.b[4] = sbox_e[rd_state.b[4]];
+    rd_state.b[8] = sbox_e[rd_state.b[8]];
+    rd_state.b[12] = sbox_e[rd_state.b[12]];
+
+      // 2nd
+    tmp = rd_state.b[1];
+    rd_state.b[1] = sbox_e[rd_state.b[5]];
+    rd_state.b[5] = sbox_e[rd_state.b[9]];
+    rd_state.b[9] = sbox_e[rd_state.b[13]];
+    rd_state.b[13] = sbox_e[tmp];
+
+      // 3th
+    tmp = rd_state.b[2];
+    rd_state.b[2] = sbox_e[rd_state.b[10]];
+    rd_state.b[10] = sbox_e[tmp];
+    tmp = rd_state.b[6];
+    rd_state.b[6] = sbox_e[rd_state.b[14]];
+    rd_state.b[14] = sbox_e[tmp];
+
+      // 4th
+    tmp = rd_state.b[3];
+    rd_state.b[3] = sbox_e[rd_state.b[15]];
+    rd_state.b[15] = sbox_e[rd_state.b[11]];
+    rd_state.b[11] = sbox_e[rd_state.b[7]];
+    rd_state.b[7] = sbox_e[tmp];
+
+      // mixcolumn except for last round
+    if( --nrounds ) {
+      for( j = 0; j < 16; j += 4 ) {
+        uint8_t tmp =
+            rd_state.b[j+0] ^
+            rd_state.b[j+1] ^
+            rd_state.b[j+2] ^
+            rd_state.b[j+3];
+
+        rd_state.b[j+0] ^= xtime( rd_state.b[j+0] ^ rd_state.b[j+1] ) ^ tmp;
+        rd_state.b[j+1] ^= xtime( rd_state.b[j+1] ^ rd_state.b[j+2] ) ^ tmp;
+        rd_state.b[j+2] ^= xtime( rd_state.b[j+2] ^ rd_state.b[j+3] ) ^ tmp;
+        rd_state.b[j+3] =
+            rd_state.b[j+0] ^
+            rd_state.b[j+1] ^
+            rd_state.b[j+2] ^
+            tmp;
+        }
+      }
+
+    rd_state.w[0] ^= *expkey++;
+    rd_state.w[1] ^= *expkey++;
+    rd_state.w[2] ^= *expkey++;
+    rd_state.w[3] ^= *expkey++;
+  } while( nrounds );
+
+  memcpy( out, &rd_state, 16 );
+}
diff --git a/common/security/aes.h b/common/security/aes.h
new file mode 100644
index 0000000..9277d5e
--- /dev/null
+++ b/common/security/aes.h
@@ -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.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_SECURITY_AES_H__
+#define OMAHA_COMMON_SECURITY_AES_H__
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Encrypt a block.
+// Both key and block size are 128 bits.
+void AES_encrypt_block(const uint8_t* key,
+                       const uint8_t* in,
+                       uint8_t* out );
+
+#define AES_BLOCK_SIZE 16
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OMAHA_COMMON_SECURITY_AES_H__
diff --git a/common/security/b64.c b/common/security/b64.c
new file mode 100644
index 0000000..2d95cfc
--- /dev/null
+++ b/common/security/b64.c
@@ -0,0 +1,88 @@
+// 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 "b64.h"
+
+static const char b64outmap[64] = {
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
+};
+
+static const char b64inmap[96] = {
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0,
+  53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 0, 0, 0, 0, 0, 0,
+  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 0, 0, 0, 0, 64,
+  0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+  42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 0, 0, 0, 0, 0,
+};
+
+int B64_encode(const uint8_t* input,
+               int input_length,
+               char* output,
+               int output_max) {
+  unsigned int accu = 0;
+  int shift = 0;
+  int output_size = 0;
+
+  while (input_length--) {
+    accu <<= 8;
+    accu |= *input++;
+    shift += 8;
+
+    while (shift >= 6) {
+      if (output_size >= output_max) return -1;  // out of output space
+      output[output_size++] = b64outmap[(accu >> (shift - 6)) & 63];
+      shift -= 6;
+    }
+  }
+  if (shift) {
+    if (output_size >= output_max) return -1;  // out of output space
+    accu <<= 8;  // pad with 0 byte really
+    shift += 8;
+    output[output_size++] = b64outmap[(accu >> (shift - 6)) & 63];
+  }
+
+  // Output terminating 0
+  if (output_size >= output_max) return -1;  // out of output space
+  output[output_size] = '\0';
+
+  return output_size;
+}
+
+int B64_decode(const char* input,
+               uint8_t* output,
+               int output_max) {
+  unsigned int accu = 0;
+  int shift = 0;
+  int output_size = 0;
+
+  while (*input) {
+    unsigned char in = *input++ & 255;
+    if (in < 32 || in > 127 || !b64inmap[in - 32]) return -1;  // invalid input
+    accu <<= 6;
+    accu |= (b64inmap[in - 32] - 1);
+    shift += 6;
+    if (shift >= 8) {
+      if (output_size >= output_max) return -1;  // out of output space
+      output[output_size++] = (accu >> (shift - 8)) & 255;
+      shift -= 8;
+    }
+  }
+  return output_size;
+}
diff --git a/common/security/b64.h b/common/security/b64.h
new file mode 100644
index 0000000..a48a931
--- /dev/null
+++ b/common/security/b64.h
@@ -0,0 +1,38 @@
+// 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_SECURITY_B64_H__
+#define OMAHA_COMMON_SECURITY_B64_H__
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int B64_encode(const uint8_t* input,
+              int input_length,
+              char* output,
+              int output_max);
+
+int B64_decode(const char* input,
+               uint8_t* output,
+               int output_max);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OMAHA_COMMON_SECURITY_B64_H__
diff --git a/common/security/build.scons b/common/security/build.scons
new file mode 100644
index 0000000..eb77083
--- /dev/null
+++ b/common/security/build.scons
@@ -0,0 +1,49 @@
+#!/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/security/challenger.cc b/common/security/challenger.cc
new file mode 100644
index 0000000..ab3ee95
--- /dev/null
+++ b/common/security/challenger.cc
@@ -0,0 +1,84 @@
+// 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 "challenger.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "rsa.h"
+#include "md5.h"
+#include "aes.h"
+#include "b64.h"
+
+// Windows compilers do not have C99 support yet.
+#if defined(WIN32) || defined(_WIN32)
+#ifndef snprintf
+#define snprintf _snprintf
+#endif
+#endif
+
+Challenger::Challenger(RSA::PublicKey pkey,
+                       const unsigned char* seed, int seed_size)
+    : rsa_(pkey) {
+  memset(count_, 0, sizeof(count_));
+  // Use seed as key for AES. Compress seed first.
+  MD5(seed, seed_size, seed_);
+}
+
+const char* Challenger::challenge() {
+  uint8_t ctr[AES_BLOCK_SIZE];
+
+  // Compute current challenge.
+  AES_encrypt_block(seed_, count_, ctr);
+
+  // Increment count for future fresh challenges.
+  for (size_t i = 0; i < sizeof(count_) && !++count_[i]; ++i);
+
+  // Prepend our version number.
+  char* p = challenge_;
+  p += snprintf(challenge_, sizeof(challenge_), "%d:", rsa_.version());
+
+  // Append our current challenge.
+  B64_encode(ctr, sizeof(ctr), p, sizeof(challenge_) - (p - challenge_));
+
+  return challenge_;
+}
+
+bool Challenger::verify(const char* hash, const char* signature) const {
+  char message[128];
+  uint8_t sigbuf[128];
+
+  // Expect exactly 128 bytes of decoded signature data.
+  if (B64_decode(signature, sigbuf, sizeof(sigbuf)) != sizeof(sigbuf))
+    return false;
+
+  // Verify signature with baked-in public key and recover embedded message.
+  int result = rsa_.verify(sigbuf, sizeof(sigbuf),
+                           message, sizeof(message) - 1);
+
+  if (result < 0 || result >= static_cast<int>(sizeof(message) - 1))
+    return false;
+
+  // Since we're expecting a textual message, 0-terminate it.
+  message[result] = '\0';
+
+  // Construct and compare expected against received signed message.
+  char expected[128];
+  snprintf(expected, sizeof(expected), "%s:%s", challenge_, hash);
+
+  return !strcmp(expected, message);
+}
diff --git a/common/security/challenger.h b/common/security/challenger.h
new file mode 100644
index 0000000..d2d6e0a
--- /dev/null
+++ b/common/security/challenger.h
@@ -0,0 +1,50 @@
+// 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_SECURITY_CHALLENGER_H__
+#define OMAHA_COMMON_SECURITY_CHALLENGER_H__
+
+#include <inttypes.h>
+
+#include "md5.h"
+#include "aes.h"
+#include "rsa.h"
+
+class Challenger {
+ public:
+  // Instantiate internal PRNG with seed. Use a proper method on your
+  // target platform to collect some entropy. For windows for instance,
+  // use CryptoAPI; on unix, read some from /dev/urandom.
+  // 128 bits of entropy is plenty.
+  explicit Challenger(RSA::PublicKey public_key,
+                      const uint8_t* seed, int seed_size);
+
+  // Not a const method! Every call updates internal state and never
+  // are identical challenges returned.
+  // Returns WebSafe base64 encoded string.
+  const char* challenge();
+
+  // Verifies whether signature contains current challenge and hash.
+  // Arguments are expected to be WebSafe base64 encoded strings.
+  bool verify(const char* hash, const char* signature) const;
+
+ private:
+  char challenge_[64];
+  uint8_t count_[AES_BLOCK_SIZE];
+  uint8_t seed_[MD5_DIGEST_SIZE];
+  RSA rsa_;
+};
+
+#endif  // OMAHA_COMMON_SECURITY_CHALLENGER_H__
diff --git a/common/security/hash-internal.h b/common/security/hash-internal.h
new file mode 100644
index 0000000..9bade84
--- /dev/null
+++ b/common/security/hash-internal.h
@@ -0,0 +1,52 @@
+// 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_SECURITY_HASH_INTERNAL_H__
+#define OMAHA_COMMON_SECURITY_HASH_INTERNAL_H__
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+struct HASH_CTX;  // forward decl
+
+typedef struct HASH_VTAB {
+  void (* const init)(struct HASH_CTX*);
+  void (* const update)(struct HASH_CTX*, const void*, int);
+  const uint8_t* (* const final)(struct HASH_CTX*);
+  const uint8_t* (* const hash)(const void*, int, uint8_t*);
+  int size;
+} HASH_VTAB;
+
+typedef struct HASH_CTX {
+  const HASH_VTAB * f;
+  uint64_t count;
+  uint8_t buf[64];
+  uint32_t state[8];  // upto SHA2
+} HASH_CTX;
+
+#define HASH_init(ctx) (ctx)->f->init(ctx)
+#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len)
+#define HASH_final(ctx) (ctx)->f->final(ctx)
+#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest)
+#define HASH_size(ctx) (ctx)->f->size
+
+#ifdef __cplusplus
+}
+#endif  // __cplusplus
+
+#endif  // OMAHA_COMMON_SECURITY_HASH_INTERNAL_H__
diff --git a/common/security/hmac.c b/common/security/hmac.c
new file mode 100644
index 0000000..f726da3
--- /dev/null
+++ b/common/security/hmac.c
@@ -0,0 +1,68 @@
+// 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.
+// ========================================================================
+//
+// Optimized for minimal code size.
+
+#include "hmac.h"
+
+#include <memory.h>
+#include "sha.h"
+#include "md5.h"
+
+static void HMAC_init(HMAC_CTX* ctx, const void* key, int len) {
+  int i;
+  memset(&ctx->opad[0], 0, sizeof(ctx->opad));
+
+  if (len > sizeof(ctx->opad)) {
+    HASH_init(&ctx->hash);
+    HASH_update(&ctx->hash, key, len);
+    memcpy(&ctx->opad[0], HASH_final(&ctx->hash), HASH_size(&ctx->hash));
+  } else {
+    memcpy(&ctx->opad[0], key, len);
+  }
+
+  for (i = 0; i < sizeof(ctx->opad); ++i) {
+    ctx->opad[i] ^= 0x36;
+  }
+
+  HASH_init(&ctx->hash);
+  HASH_update(&ctx->hash, ctx->opad, sizeof(ctx->opad));  // hash ipad
+
+  for (i = 0; i < sizeof(ctx->opad); ++i) {
+    ctx->opad[i] ^= (0x36 ^ 0x5c);
+  }
+}
+
+void HMAC_MD5_init(HMAC_CTX* ctx, const void* key, int len) {
+  MD5_init(&ctx->hash);
+  HMAC_init(ctx, key, len);
+}
+
+void HMAC_SHA_init(HMAC_CTX* ctx, const void* key, int len) {
+  SHA_init(&ctx->hash);
+  HMAC_init(ctx, key, len);
+}
+
+const uint8_t* HMAC_final(HMAC_CTX* ctx) {
+  uint8_t digest[32];  // upto SHA2
+  memcpy(digest, HASH_final(&ctx->hash),
+         (HASH_size(&ctx->hash) <= sizeof(digest) ?
+             HASH_size(&ctx->hash) : sizeof(digest)));
+  HASH_init(&ctx->hash);
+  HASH_update(&ctx->hash, ctx->opad, sizeof(ctx->opad));
+  HASH_update(&ctx->hash, digest, HASH_size(&ctx->hash));
+  memset(&ctx->opad[0], 0, sizeof(ctx->opad));  // wipe key
+  return HASH_final(&ctx->hash);
+}
diff --git a/common/security/hmac.h b/common/security/hmac.h
new file mode 100644
index 0000000..fdfce11
--- /dev/null
+++ b/common/security/hmac.h
@@ -0,0 +1,42 @@
+// 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_SECURITY_HMAC_H__
+#define OMAHA_COMMON_SECURITY_HMAC_H__
+
+#include <inttypes.h>
+#include "hash-internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct HMAC_CTX {
+  HASH_CTX hash;
+  uint8_t opad[64];
+} HMAC_CTX;
+
+void HMAC_MD5_init(HMAC_CTX* ctx, const void* key, int len);
+void HMAC_SHA_init(HMAC_CTX* ctx, const void* key, int len);
+const uint8_t* HMAC_final(HMAC_CTX* ctx);
+
+#define HMAC_update(ctx, data, len) HASH_update(&(ctx)->hash, data, len)
+#define HMAC_size(ctx) HASH_size(&(ctx)->hash)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OMAHA_COMMON_SECURITY_HMAC_H__
diff --git a/common/security/md5.c b/common/security/md5.c
new file mode 100644
index 0000000..7f3b7c1
--- /dev/null
+++ b/common/security/md5.c
@@ -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.
+// ========================================================================
+//
+// Optimized for minimal code size.
+
+#include "md5.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static const char Kr[64] =
+{
+  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,
+  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,
+  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,
+  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21
+};
+
+static const int KK[64] =
+{
+  0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+  0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+  0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+  0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+  0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+  0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+  0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+  0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+  0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+  0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+  0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+  0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+  0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+  0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+  0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+  0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+};
+
+static void MD5_Transform(MD5_CTX* ctx) {
+  uint32_t W[64];
+  uint32_t A, B, C, D;
+  uint8_t* p = ctx->buf;
+  int t;
+
+  for(t = 0; t < 16; ++t) {
+    uint32_t tmp =  *p++;
+    tmp |= *p++ << 8;
+    tmp |= *p++ << 16;
+    tmp |= *p++ << 24;
+    W[t] = tmp;
+  }
+
+  A = ctx->state[0];
+  B = ctx->state[1];
+  C = ctx->state[2];
+  D = ctx->state[3];
+
+  for(t = 0; t < 64; t++) {
+    uint32_t f, tmp;
+    int g;
+
+    if (t < 16) {
+      f = (D^(B&(C^D)));
+      g = t;
+    } else if ( t < 32) {
+      f = (C^(D&(B^C)));
+      g = (5*t + 1) & 15;
+    } else if ( t < 48) {
+      f = (B^C^D);
+      g = (3*t + 5) & 15;
+    } else {
+      f = (C^(B|(~D)));
+      g = (7*t) & 15;
+    }
+
+    tmp = D;
+    D = C;
+    C = B;
+    B = B + rol(Kr[t], (A+f+KK[t]+W[g]));
+    A = tmp;
+  }
+
+  ctx->state[0] += A;
+  ctx->state[1] += B;
+  ctx->state[2] += C;
+  ctx->state[3] += D;
+}
+
+static const HASH_VTAB MD5_VTAB = {
+  MD5_init,
+  MD5_update,
+  MD5_final,
+  MD5,
+  MD5_DIGEST_SIZE
+};
+
+void MD5_init(MD5_CTX* ctx) {
+  ctx->f = &MD5_VTAB;
+  ctx->state[0] = 0x67452301;
+  ctx->state[1] = 0xEFCDAB89;
+  ctx->state[2] = 0x98BADCFE;
+  ctx->state[3] = 0x10325476;
+  ctx->count = 0;
+}
+
+
+void MD5_update(MD5_CTX* ctx, const void* data, int len) {
+  int i = ctx->count & 63;
+  const uint8_t* p = (const uint8_t*)data;
+
+  ctx->count += len;
+
+  while (len--) {
+    ctx->buf[i++] = *p++;
+    if (i == 64) {
+      MD5_Transform(ctx);
+      i = 0;
+    }
+  }
+}
+
+
+const uint8_t* MD5_final(MD5_CTX* ctx) {
+  uint8_t* p = ctx->buf;
+  uint64_t cnt = ctx->count * 8;
+  int i;
+
+  MD5_update(ctx, (uint8_t*)"\x80", 1);
+  while ((ctx->count & 63) != 56) {
+    MD5_update(ctx, (uint8_t*)"\0", 1);
+  }
+  for (i = 0; i < 8; ++i) {
+    uint8_t tmp = cnt >> (i * 8);
+    MD5_update(ctx, &tmp, 1);
+  }
+
+  for (i = 0; i < 4; i++) {
+    uint32_t tmp = ctx->state[i];
+    *p++ = tmp;
+    *p++ = tmp >> 8;
+    *p++ = tmp >> 16;
+    *p++ = tmp >> 24;
+  }
+
+  return ctx->buf;
+}
+
+
+/* Convenience function */
+const uint8_t* MD5(const void* data, int len, uint8_t* digest) {
+  MD5_CTX ctx;
+  MD5_init(&ctx);
+  MD5_update(&ctx, data, len);
+  memcpy(digest, MD5_final(&ctx), MD5_DIGEST_SIZE);
+  return digest;
+}
diff --git a/common/security/md5.h b/common/security/md5.h
new file mode 100644
index 0000000..37cfc04
--- /dev/null
+++ b/common/security/md5.h
@@ -0,0 +1,41 @@
+// 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_SECURITY_MD5_H__
+#define OMAHA_COMMON_SECURITY_MD5_H__
+
+#include <inttypes.h>
+#include "hash-internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef HASH_CTX MD5_CTX;
+
+void MD5_init(MD5_CTX* ctx);
+void MD5_update(MD5_CTX* ctx, const void* data, int len);
+const uint8_t* MD5_final(MD5_CTX* ctx);
+
+// Convenience method. Returns digest address.
+const uint8_t* MD5(const void* data, int len, uint8_t* digest);
+
+#define MD5_DIGEST_SIZE 16
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif  // OMAHA_COMMON_SECURITY_MD5_H__
diff --git a/common/security/rc4.c b/common/security/rc4.c
new file mode 100644
index 0000000..3f1b336
--- /dev/null
+++ b/common/security/rc4.c
@@ -0,0 +1,84 @@
+// 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 "rc4.h"
+
+#include <inttypes.h>
+
+void RC4_setKey(RC4_CTX* ctx, const uint8_t* key, int len) {
+  uint8_t* S = ctx->S;
+  int i, j;
+
+  for (i = 0; i < 256; ++i) {
+    S[i] = i;
+  }
+
+  j = 0;
+  for (i = 0; i < 256; ++i) {
+    uint8_t tmp;
+
+    j = (j + S[i] + key[i % len]) & 255;
+
+    tmp = S[i];
+    S[i] = S[j];
+    S[j] = tmp;
+  }
+
+  ctx->i = 0;
+  ctx->j = 0;
+}
+
+void RC4_crypt(RC4_CTX* ctx,
+               const uint8_t *in,
+               uint8_t* out,
+               int len) {
+  uint8_t i = ctx->i;
+  uint8_t j = ctx->j;
+  uint8_t* S = ctx->S;
+
+  int n;
+
+  for (n = 0; n < len; ++n) {
+    uint8_t tmp;
+
+    i = (i + 1) & 255;
+    j = (j + S[i]) & 255;
+
+    tmp = S[i];
+    S[i] = S[j];
+    S[j] = tmp;
+
+    if (in) {
+      if (out) {
+        out[n] = in[n] ^ S[(S[i] + S[j]) & 255];
+      }
+    } else {
+      if (out) {
+        out[n] = S[(S[i] + S[j]) & 255];
+      }
+    }
+  }
+
+  ctx->i = i;
+  ctx->j = j;
+}
+
+void RC4_discard(RC4_CTX* ctx, int len) {
+  RC4_crypt(ctx, 0, 0, len);
+}
+
+void RC4_stream(RC4_CTX* ctx, uint8_t* out, int len) {
+  RC4_crypt(ctx, 0, out, len);
+}
diff --git a/common/security/rc4.h b/common/security/rc4.h
new file mode 100644
index 0000000..e3e9f6e
--- /dev/null
+++ b/common/security/rc4.h
@@ -0,0 +1,40 @@
+// 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_SECURITY_RC4_H__
+#define OMAHA_COMMON_SECURITY_RC4_H__
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+  uint8_t S[256];
+  uint8_t i;
+  uint8_t j;
+} RC4_CTX;
+
+void RC4_setKey(RC4_CTX* ctx, const uint8_t* data, int len);
+void RC4_discard(RC4_CTX* ctx, int len);
+void RC4_crypt(RC4_CTX* ctx, const uint8_t* in, uint8_t* out, int len);
+void RC4_stream(RC4_CTX* ctx, uint8_t* out, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // OMAHA_COMMON_SECURITY_RC4_H__
diff --git a/common/security/rsa.cc b/common/security/rsa.cc
new file mode 100644
index 0000000..4f3d104
--- /dev/null
+++ b/common/security/rsa.cc
@@ -0,0 +1,290 @@
+// 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 "rsa.h"
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "md5.h"
+#include "aes.h"
+#include "sha.h"
+#include "rc4.h"
+
+#define DINV mod[0]
+#define RR(i) mod[1 + 2*(i)]
+#define MOD(i) (mod[2 + 2*(i)] + mod[1 + 2*(i)])  // +mod[1+2*(i)] to deobscure
+
+//
+// a[] -= M
+//
+static void subM(uint32_t* a, const uint32_t* mod, int len) {
+  int64_t A = 0;
+  for (int i = 0; i < len; ++i) {
+    A += (uint64_t)a[i] - MOD(i);
+    a[i] = (uint32_t)A;
+    A >>= 32;
+  }
+}
+
+//
+// return a[] >= M
+//
+static bool geM(const uint32_t* a, const uint32_t* mod, int len) {
+  for (int i = len; i;) {
+    --i;
+    if (a[i] < MOD(i)) return false;
+    if (a[i] > MOD(i)) return true;
+  }
+  return true;  // equal
+}
+
+//
+// montgomery c[] += a * b[] / R mod M
+//
+static void montMulAdd(uint32_t* c,
+                       uint32_t a,
+                       const uint32_t* b,
+                       const uint32_t* mod,
+                       int len) {
+  uint64_t A = (uint64_t)a * b[0] + c[0];
+  uint32_t d0 = (uint32_t)A * DINV;
+  uint64_t B = (uint64_t)d0 * MOD(0) + (uint32_t)A;
+
+  int i = 1;
+  for (; i < len; ++i) {
+    A = (A >> 32) + (uint64_t)a * b[i] + c[i];
+    B = (B >> 32) + (uint64_t)d0 * MOD(i) + (uint32_t)A;
+    c[i - 1] = (uint32_t)B;
+  }
+
+  A = (A >> 32) + (B >> 32);
+
+  c[i - 1] = (uint32_t)A;
+
+  if ((A >> 32)) {  // proper probablistic padding could avoid this?
+    subM(c, mod, len);  // or moduli without the highest bit set..
+  }
+}
+
+//
+// montgomery c[] = a[] * R^2 / R mod M (= a[] * R mod M)
+//
+static void montMulR(uint32_t* c,
+                     const uint32_t* a,
+                     const uint32_t* mod,
+                     int len) {
+  memset(c, 0, len * sizeof(uint32_t));
+
+  for (int i = 0; i < len; ++i) {
+    montMulAdd(c, RR(i), a, mod, len);
+  }
+}
+
+//
+// montgomery c[] = a[] * b[] / R mod M
+//
+static void montMul(uint32_t* c,
+                    const uint32_t* a,
+                    const uint32_t* b,
+                    const uint32_t* mod,
+                    int len) {
+  memset(c, 0, len * sizeof(uint32_t));
+
+  for (int i = 0; i < len; ++i) {
+    montMulAdd(c, a[i], b, mod, len);
+  }
+}
+
+
+//
+// In-place public exponentiation.
+// Input and output big-endian byte array.
+// Returns 0 on failure or # uint8_t written in inout (always inout_len).
+//
+int RSA::raw(uint8_t* inout, int inout_len) const {
+  const uint32_t* mod = &pkey_[1];
+  int len = *mod++;
+
+  if (len > kMaxWords)
+    return 0;  // Only work with up to 2048 bit moduli.
+  if ((len * 4) != inout_len)
+    return 0;  // Input length should match modulus length.
+
+  uint32_t a[kMaxWords];
+
+  // Convert from big endian byte array to little endian word array.
+  for (int i = 0; i < len; ++i) {
+    uint32_t tmp =
+      (inout[((len - 1 - i) * 4) + 0] << 24) |
+      (inout[((len - 1 - i) * 4) + 1] << 16) |
+      (inout[((len - 1 - i) * 4) + 2] << 8) |
+      (inout[((len - 1 - i) * 4) + 3] << 0);
+    a[i] = tmp;
+  }
+
+  uint32_t aR[kMaxWords];
+  uint32_t aaR[kMaxWords];
+  uint32_t aaa[kMaxWords];
+
+  montMulR(aR, a, mod, len);       // aR = a * R mod M
+  montMul(aaR, aR, aR, mod, len);  // aaR = a^2 * R mod M
+  montMul(aaa, aaR, a, mod, len);  // aaa = a^3 mod M
+
+  // Make sure aaa < mod; aaa is at most 1x mod too large.
+  if (geM(aaa, mod, len)) {
+    subM(aaa, mod, len);
+  }
+
+  // Convert to bigendian byte array
+  int reslen = 0;
+
+  for (int i = len - 1; i >= 0; --i) {
+    uint32_t tmp = aaa[i];
+    inout[reslen++] = tmp >> 24;
+    inout[reslen++] = tmp >> 16;
+    inout[reslen++] = tmp >> 8;
+    inout[reslen++] = tmp >> 0;
+  }
+
+  return reslen;
+}
+
+//
+// Verify a Google style padded message recovery signature and return the
+// message.
+//
+int RSA::verify(const uint8_t* data, int data_len,
+                void* output, int output_len) const {
+  uint8_t res[kMaxWords * 4];
+
+  if (data_len < 0 || data_len > (kMaxWords * 4))
+    return 0;  // Input too big, 2048 bit max.
+
+  memcpy(res, data, data_len);
+
+  int reslen = this->raw(res, data_len);
+
+  if (!reslen) return 0;
+
+  uint8_t md5[16];
+
+  MD5(res, reslen - 16, md5);
+
+  for (int i = 0; i < 16; ++i) {
+    res[reslen - 16 + i] ^= md5[i];
+  }
+
+  // Unmask low part using high part as ofb key.
+  uint8_t iv[16] = {0};
+
+  for (int i = 0; i < reslen - 16; i++) {
+    if (!(i & 15))
+      AES_encrypt_block(res + reslen - 16, iv, iv);
+    res[i] ^= iv[i & 15];
+  }
+
+  res[0] &= 127;
+  res[0] %= reslen - 16 - 16;
+
+  bool result = true;
+
+  // Verify high part is hash of random in low part.
+  MD5(res + 1, res[0] + 16, md5);
+  for (int i = 0; i < 16; ++i) {
+    result = result && (res[reslen - 16 + i] == md5[i]);
+  }
+
+  if (!result) {
+    return 0;  // verification failure
+  }
+
+  // Copy message into output[]
+  if (res[0] > output_len) {
+    return 0;  // output too small, return failure
+  }
+
+  memcpy(output, res + 1, res[0]);
+
+  return res[0];
+}
+
+//
+// Hybrid encrypt message.
+// Make up RC4 key using seed and hash of msg.
+// Wrap key with RSA, encrypt msg with RC4.
+//
+int RSA::encrypt(const uint8_t* msg, int msg_len,
+                 const void* seed, int seed_len,
+                 uint8_t* output, int output_max) const {
+  int output_len = this->encryptedSize(msg_len);
+  if (output_max < 0 || output_max < output_len)
+    return 0;
+
+  int header_size = output_len - msg_len;  // Our added overhead.
+
+  // Hash of message. Least significant SHA_DIGEST_SIZE bytes of RSA number.
+  uint8_t* hash = &output[header_size - SHA_DIGEST_SIZE];
+  SHA(msg, msg_len, hash);
+
+  // Hash(Hash(message) | seed).
+  SHA_CTX sha;
+  SHA_init(&sha);
+  SHA_update(&sha, hash, SHA_DIGEST_SIZE);
+  SHA_update(&sha, seed, seed_len);
+
+  // Use this Hash(Hash(message) | seed) as RC4 key for prng.
+  RC4_CTX rc4;
+  RC4_setKey(&rc4, SHA_final(&sha), SHA_DIGEST_SIZE);
+  RC4_discard(&rc4, 1536);  // Drop some to warm up RC4.
+
+  uint8_t* key = &output[1 + 4];
+
+  // Prng conjure some bytes.
+  RC4_stream(&rc4, key, this->size() - SHA_DIGEST_SIZE);
+  key[0] &= 127;  // Drop top bit to be less than modulus.
+
+  // Mask plaintext hash with hash of prng part.
+  SHA_init(&sha);
+  SHA_update(&sha, key, this->size() - SHA_DIGEST_SIZE);
+  const uint8_t* mask = SHA_final(&sha);
+  for (int i = 0; i < SHA_DIGEST_SIZE; ++i)
+    hash[i] ^= mask[i];
+
+  // Use entire RSA number as content encryption key.
+  RC4_setKey(&rc4, key, this->size());
+  RC4_discard(&rc4, 1536);  // Warm up RC4.
+
+  // Output wire-format version, single 0 byte.
+  output[0] = 0;
+
+  // Output version, msb first.
+  uint32_t version = this->version();
+  output[1] = version >> 24;
+  output[2] = version >> 16;
+  output[3] = version >> 8;
+  output[4] = version >> 0;
+
+  // Wrap key data with public RSA key.
+  if (!this->raw(key, this->size()))
+    return 0;
+
+  // Append encrypted message.
+  RC4_crypt(&rc4, msg, &output[header_size], msg_len);
+
+  return output_len;
+}
diff --git a/common/security/rsa.h b/common/security/rsa.h
new file mode 100644
index 0000000..3403c11
--- /dev/null
+++ b/common/security/rsa.h
@@ -0,0 +1,66 @@
+// 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_SECURITY_RSA_H__
+#define OMAHA_COMMON_SECURITY_RSA_H__
+
+#include <inttypes.h>
+
+class RSA {
+ public:
+  typedef const uint32_t PublicKeyInstance[];
+  typedef const uint32_t* PublicKey;
+
+  // Public_key as montgomery precomputed array
+  explicit RSA(PublicKey public_key) : pkey_(public_key) {}
+
+  // Verifies a Google style RSA message recovery signature.
+  //
+  // sig[] signature to verify, big-endian byte array.
+  // sig_len length of sig[] in bytes.
+  // If verified successfully, output receives the recovered
+  // message and the function returns the number of bytes.
+  // If not successful, the function returns 0.
+  // (empty message is not a useful message)
+  int verify(const uint8_t* sig, int sig_len,
+             void* output, int output_max) const;
+
+  // Hybrid encrypt message.
+  //
+  // output_max should be at least encryptedSize(msg_len)
+  // Returns 0 on failure, # output bytes on success.
+  int encrypt(const uint8_t* msg, int msg_len,
+              const void* seed, int seed_len,
+              uint8_t* output, int output_max) const;
+
+  int encryptedSize(int len) const {
+    return len + 1 + 4 + size();
+  }
+
+  // Performs in-place public key exponentiation.
+  //
+  // Input_len should match size of modulus in bytes.
+  // Returns 0 on failure, # of bytes written on success.
+  int raw(uint8_t* input, int input_len) const;
+
+  int version() const { return pkey_[0]; }
+  int size() const { return pkey_[1] * 4; }
+
+ private:
+  const PublicKey pkey_;
+  static const int kMaxWords = 64;
+};
+
+#endif  // OMAHA_COMMON_SECURITY_RSA_H__
diff --git a/common/security/sha.c b/common/security/sha.c
new file mode 100644
index 0000000..dee6624
--- /dev/null
+++ b/common/security/sha.c
@@ -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.
+// ========================================================================
+//
+// Optimized for minimal code size.
+
+#include "sha.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static void SHA1_Transform(SHA_CTX* ctx) {
+  uint32_t W[80];
+  uint32_t A, B, C, D, E;
+  uint8_t* p = ctx->buf;
+  int t;
+
+  for(t = 0; t < 16; ++t) {
+    uint32_t tmp =  *p++ << 24;
+    tmp |= *p++ << 16;
+    tmp |= *p++ << 8;
+    tmp |= *p++;
+    W[t] = tmp;
+  }
+
+  for(; t < 80; t++) {
+    W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+  }
+
+  A = ctx->state[0];
+  B = ctx->state[1];
+  C = ctx->state[2];
+  D = ctx->state[3];
+  E = ctx->state[4];
+
+  for(t = 0; t < 80; t++) {
+    uint32_t tmp = rol(5,A) + E + W[t];
+
+    if (t < 20)
+      tmp += (D^(B&(C^D))) + 0x5A827999;
+    else if ( t < 40)
+      tmp += (B^C^D) + 0x6ED9EBA1;
+    else if ( t < 60)
+      tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC;
+    else
+      tmp += (B^C^D) + 0xCA62C1D6;
+
+    E = D;
+    D = C;
+    C = rol(30,B);
+    B = A;
+    A = tmp;
+  }
+
+  ctx->state[0] += A;
+  ctx->state[1] += B;
+  ctx->state[2] += C;
+  ctx->state[3] += D;
+  ctx->state[4] += E;
+}
+
+static const HASH_VTAB SHA_VTAB = {
+  SHA_init,
+  SHA_update,
+  SHA_final,
+  SHA,
+  SHA_DIGEST_SIZE
+};
+
+void SHA_init(SHA_CTX* ctx) {
+  ctx->f = &SHA_VTAB;
+  ctx->state[0] = 0x67452301;
+  ctx->state[1] = 0xEFCDAB89;
+  ctx->state[2] = 0x98BADCFE;
+  ctx->state[3] = 0x10325476;
+  ctx->state[4] = 0xC3D2E1F0;
+  ctx->count = 0;
+}
+
+
+void SHA_update(SHA_CTX* ctx, const void* data, int len) {
+  int i = ctx->count & 63;
+  const uint8_t* p = (const uint8_t*)data;
+
+  ctx->count += len;
+
+  while (len--) {
+    ctx->buf[i++] = *p++;
+    if (i == 64) {
+      SHA1_Transform(ctx);
+      i = 0;
+    }
+  }
+}
+
+
+const uint8_t* SHA_final(SHA_CTX* ctx) {
+  uint8_t *p = ctx->buf;
+  uint64_t cnt = ctx->count * 8;
+  int i;
+
+  SHA_update(ctx, (uint8_t*)"\x80", 1);
+  while ((ctx->count & 63) != 56) {
+    SHA_update(ctx, (uint8_t*)"\0", 1);
+  }
+  for (i = 0; i < 8; ++i) {
+    uint8_t tmp = cnt >> ((7 - i) * 8);
+    SHA_update(ctx, &tmp, 1);
+  }
+
+  for (i = 0; i < 5; i++) {
+    uint32_t tmp = ctx->state[i];
+    *p++ = tmp >> 24;
+    *p++ = tmp >> 16;
+    *p++ = tmp >> 8;
+    *p++ = tmp >> 0;
+  }
+
+  return ctx->buf;
+}
+
+/* Convenience function */
+const uint8_t* SHA(const void* data, int len, uint8_t* digest) {
+  SHA_CTX ctx;
+  SHA_init(&ctx);
+  SHA_update(&ctx, data, len);
+  memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE);
+  return digest;
+}
diff --git a/common/security/sha.h b/common/security/sha.h
new file mode 100644
index 0000000..e9750dd
--- /dev/null
+++ b/common/security/sha.h
@@ -0,0 +1,41 @@
+// 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_SECURITY_SHA1_H__
+#define OMAHA_COMMON_SECURITY_SHA1_H__
+
+#include <inttypes.h>
+#include "hash-internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef HASH_CTX SHA_CTX;
+
+void SHA_init(SHA_CTX* ctx);
+void SHA_update(SHA_CTX* ctx, const void* data, int len);
+const uint8_t* SHA_final(SHA_CTX* ctx);
+
+// Convenience method. Returns digest address.
+const uint8_t* SHA(const void* data, int len, uint8_t* digest);
+
+#define SHA_DIGEST_SIZE 20
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif  // OMAHA_COMMON_SECURITY_SHA1_H__
diff --git a/common/serializable_object.cc b/common/serializable_object.cc
new file mode 100644
index 0000000..d3106da
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..7f7877f
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..68f3581
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..51fbebf
--- /dev/null
+++ b/common/service_utils.cc
@@ -0,0 +1,309 @@
+// 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);
+}
+
+}  // namespace omaha
+
diff --git a/common/service_utils.h b/common/service_utils.h
new file mode 100644
index 0000000..c7035e7
--- /dev/null
+++ b/common/service_utils.h
@@ -0,0 +1,134 @@
+// 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);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SERVICE_UTILS_H__
+
diff --git a/common/service_utils_unittest.cc b/common/service_utils_unittest.cc
new file mode 100644
index 0000000..8a74374
--- /dev/null
+++ b/common/service_utils_unittest.cc
@@ -0,0 +1,50 @@
+// 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 "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) {
+  ASSERT_TRUE(ServiceInstall::IsServiceInstalled(_T("BITS")));
+  ASSERT_TRUE(ServiceInstall::IsServiceInstalled(_T("NetLogon")));
+  ASSERT_FALSE(ServiceInstall::IsServiceInstalled(_T("FooBar")));
+}
+
+}  // namespace omaha
+
diff --git a/common/sha.cc b/common/sha.cc
new file mode 100644
index 0000000..e6d91e9
--- /dev/null
+++ b/common/sha.cc
@@ -0,0 +1,147 @@
+// 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
new file mode 100644
index 0000000..c5212ec
--- /dev/null
+++ b/common/sha.h
@@ -0,0 +1,82 @@
+// 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
new file mode 100644
index 0000000..bed3ee2
--- /dev/null
+++ b/common/sha_unittest.cc
@@ -0,0 +1,67 @@
+// 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
new file mode 100644
index 0000000..3250231
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..7761641
--- /dev/null
+++ b/common/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 "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
new file mode 100644
index 0000000..32b76b4
--- /dev/null
+++ b/common/shell.cc
@@ -0,0 +1,398 @@
+// 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
new file mode 100644
index 0000000..c79382d
--- /dev/null
+++ b/common/shell.h
@@ -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.
+// ========================================================================
+//
+// 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
new file mode 100644
index 0000000..ddfe140
--- /dev/null
+++ b/common/shell_unittest.cc
@@ -0,0 +1,100 @@
+// 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_callback.h b/common/shutdown_callback.h
new file mode 100644
index 0000000..e21598a
--- /dev/null
+++ b/common/shutdown_callback.h
@@ -0,0 +1,35 @@
+// 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.
+// ========================================================================
+
+//
+// ShutdownCallBack monitors a shutdown event.
+
+#ifndef OMAHA_COMMON_SHUTDOWN_CALLBACK_H__
+#define OMAHA_COMMON_SHUTDOWN_CALLBACK_H__
+
+#include <windows.h>
+
+namespace omaha {
+
+class ShutdownCallback {
+ public:
+  virtual ~ShutdownCallback() {}
+  virtual HRESULT Shutdown() = 0;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SHUTDOWN_CALLBACK_H__
+
diff --git a/common/shutdown_handler.cc b/common/shutdown_handler.cc
new file mode 100644
index 0000000..a03fb79
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..d7cfa94
--- /dev/null
+++ b/common/shutdown_handler.h
@@ -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.
+// ========================================================================
+
+//
+// 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
new file mode 100644
index 0000000..c0b0cb9
--- /dev/null
+++ b/common/signatures.cc
@@ -0,0 +1,1025 @@
+// 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.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;
+
+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
new file mode 100644
index 0000000..0a23fe7
--- /dev/null
+++ b/common/signatures.h
@@ -0,0 +1,297 @@
+// 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
new file mode 100644
index 0000000..f6ab978
--- /dev/null
+++ b/common/signatures_unittest.cc
@@ -0,0 +1,162 @@
+// 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) {
+  // Load the test certificates from the directory where the unit test is
+  // running from. The test certificates are copied there during the build.
+  TCHAR directory[MAX_PATH] = {0};
+  ASSERT_TRUE(GetModuleDirectory(NULL, 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
new file mode 100644
index 0000000..2a49b70
--- /dev/null
+++ b/common/signaturevalidator.cc
@@ -0,0 +1,559 @@
+// 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 TCHAR* str,
+                            const TCHAR* field_name,
+                            CString* field_value) {
+  if ((!str) || (!field_name) || (!field_value))
+    return false;
+
+  // first, we have to locate pattern - "<field-name>="
+  CString field_name_with_equal_sign = field_name + CString(_T("="));
+  const TCHAR* field_name_start = _tcsstr(str, field_name_with_equal_sign);
+
+  // there is no such field? ok, sorry..
+  if (field_name_start == NULL) {
+    field_value->Empty();
+    return false;
+  }
+
+  // now, locate the end of this field-value. we know each field value
+  // is followed by a semi-colon (except last one).
+  const TCHAR* next_field = _tcschr(field_name_start, ';');
+  // identify where exactly field-value starts.
+  const TCHAR* field_value_start = field_name_start +
+                                   field_name_with_equal_sign.GetLength();
+  if (next_field) {
+    // if next-field exists, copy all the chars before semi-colon
+    field_value->SetString(field_value_start,
+        static_cast<int>(next_field - field_value_start));
+  } else {
+    // this must be the last field. copy till the end of the string.
+    field_value->SetString(field_value_start);
+  }
+
+  return true;
+}
+
+
+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;
+
+  if (!cert_context) {
+    orgn_name->Empty();
+    orgn_dept_name->Empty();
+    return false;
+  }
+
+  // Retrieve organization info in the form of a BLOB
+  CERT_NAME_BLOB orgn_blob = cert_context->pCertInfo->Subject;
+
+  TCHAR name_str[1024];
+  DWORD name_size = sizeof(name_str);
+
+  DWORD num_converted_bytes =
+    CertNameToStr(
+        kCertificateEncoding,
+        &orgn_blob,
+        CERT_X500_NAME_STR|CERT_NAME_STR_NO_QUOTING_FLAG|
+        CERT_NAME_STR_SEMICOLON_FLAG|  // all the fields to be separated by ';'
+        CERT_NAME_STR_REVERSE_FLAG,    // we are reversing the order of fields
+                                       // so that subject/signee related fields
+                                       // turn up first.
+        name_str,
+        name_size);
+
+  if ((num_converted_bytes <= 0) || (num_converted_bytes > name_size)) {
+    // num_converted_bytes > name_size - means that name_str needs to be larger.
+    // That's very unlikely so I don't call again it with a bigger string.
+    orgn_name->Empty();
+    orgn_dept_name->Empty();
+    return false;
+  }
+
+  ExtractField(name_str, _T("CN"), orgn_name);       // CN - Common Name
+  ExtractField(name_str, _T("OU"), orgn_dept_name);  // OU - Organizational Unit
+  if (trust_authority != NULL)
+    ExtractField(name_str, _T("O"), 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_timestamp) {
+  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_timestamp || (*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;
+}
+
+bool VerifySigneeIsGoogleInternal(const wchar_t* signed_file,
+                                  bool check_timestamp) {
+  // Google Name and its dept name as expected on the certificate.
+  // We switched to a slightly different cert in late 2005. The values were
+  // "Google, Inc." and "Engineering" respectively.
+  // Subject Name
+  const TCHAR* google_name = _T("Google Inc");
+  // Organization Unit Name
+  const TCHAR* google_dept_name =
+      _T("Digital ID Class 3 - Netscape Object Signing");
+
+  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, google_dept_name,
+                            CString(), true, check_timestamp);
+    if (required_cert != NULL) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool VerifySigneeIsGoogle(const wchar_t* signed_file) {
+  return VerifySigneeIsGoogleInternal(signed_file, true);
+}
+
+bool VerifySigneeIsGoogleNoTimestampCheck(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/common/signaturevalidator.h
new file mode 100644
index 0000000..9f22c3b
--- /dev/null
+++ b/common/signaturevalidator.h
@@ -0,0 +1,201 @@
+// 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.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_SIGNATUREVALIDATOR_H__
+#define OMAHA_COMMON_SIGNATUREVALIDATOR_H__
+
+#include <windows.h>
+#include <wincrypt.h>
+#include <atlstr.h>
+#pragma warning(push)
+// C4548: expression before comma has no effect
+#pragma warning(disable : 4548)
+#include <vector>
+#pragma warning(pop)
+
+namespace omaha {
+
+// Class: CertInfo
+//
+// CertInfo holds all sensible details of a certificate. During verification of
+// a signature, one CertInfo object is made for each certificate encountered in
+// the signature.
+class CertInfo {
+ public:
+  // certificate issuing company name e.g. "Google Inc".
+  CString issuing_company_name_;
+
+  // a company may own multiple certificates.
+  // so this tells which dept owns this certificate.
+  CString issuing_dept_name_;
+
+  // trust-authority (or trust-provider) name. e.g. "Verisign, Inc.".
+  CString trust_authority_name_;
+
+  // validity period start-date
+  FILETIME not_valid_before_;
+
+  // validity period end-date
+  FILETIME not_valid_after_;
+
+  // CERT_CONTEXT structure, defined by Crypto API, contains all the info about
+  // the certificate.
+  const CERT_CONTEXT *cert_context_;
+
+  explicit CertInfo(const CERT_CONTEXT* given_cert_context);
+
+  ~CertInfo();
+
+  // IsValidNow() functions returns true if this certificate is valid at this
+  // moment, based on the validity period specified in the certificate.
+  bool IsValidNow() const;
+
+  // AsString() is a utility function that's used for printing CertInfo details.
+  CString AsString() const {
+    CString cert_info_str =
+        _T("Issuing Company: \"") + issuing_company_name_ +
+        _T("\"  Dept: \"") + issuing_dept_name_ +
+        _T("\"  Trust Provider: \"") + trust_authority_name_ +
+        _T("\"  Valid From: \"") + this->FileTimeToString(&not_valid_before_) +
+        _T("\"  Valid To: \"") + this->FileTimeToString(&not_valid_after_) +
+        _T("\"");
+    return cert_info_str;
+  }
+
+
+  // FileTimeToString() is just a convenience function to print FILETIME.
+  static CString FileTimeToString(const FILETIME* ft);
+
+  // Given a cerificate context, this function extracts the subject/signee
+  // company name and its dept name(orgnanizational-unit-name, as they call it).
+  // Optionally, you could also retrieve trust-authority name.
+  static bool ExtractIssuerInfo(const CERT_CONTEXT* cert_context,
+                         CString* orgn_name,
+                         CString* orgn_dept_name,
+                         CString* trust_authority = NULL);
+
+ private:
+  // we expect the input string to contain field <name, value> pairs
+  // separated by semi-colons. i.e.
+  // <field-name>=<field-value>;<field-name=<field-value>;
+  // N O T E: If more than field exists with the same name, this function
+  //          returns the value of the first field.
+  static bool ExtractField(const TCHAR* str,
+                           const TCHAR* field_name,
+                           CString* field_value);
+};
+
+// CertList is a container for a list of certificates. It is used to hold all
+// the certificates found in the signature of a signed file. In addition, it
+// also provides interface to fetch certificates matching to a particular
+// criterion.
+//
+// Internally, CertList contains basically a vector of CertInfo* pointers.
+// The only reason why CertList is created as opposed to simply putting all
+// the certificates in a vector<CertInfo*> is to avoid memory-leaks. CertList
+// contains a list of CertInfo pointers and users don't have to worry about
+// freeing those pointers. On the other hand, if you use vector<CertInfo>
+// instead, it results in unwanted copying of CertInfo objects around.
+class CertList {
+ public:
+  // Constructor
+  CertList() {}
+
+  // Destructor
+  ~CertList() {
+    for (unsigned int inx = 0; inx < cert_list_.size(); ++inx)
+      delete cert_list_[inx];
+    cert_list_.clear();
+  }
+
+  // size() returns the number of certificates in this CertList
+  size_t size() {
+    return cert_list_.size();
+  }
+
+  // AddCertificate() is used to add a certificate to CertList.
+  // NOTE that once a certificate is added, CertList takes ownership of that
+  // CertInfo object.
+  void AddCertificate(CertInfo* cert) {
+    cert_list_.push_back(cert);
+  }
+
+  // FindFirstCert() finds the first certificate that matches given criteria.
+  // If allow_test_variant is true, the company name will also be deemed valid
+  // if it equals company_name_to_match + " (TEST)".
+  void 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_timestamp);
+
+  typedef std::vector<CertInfo*> CertInfoList;
+
+  // FindAllCerts() finds all certificates that match given criteria.
+  void FindAllCerts(CertInfoList* result_cert_info_list,
+                    CString company_name_to_match,
+                    CString orgn_unit_to_match,
+                    CString trust_authority_to_match,
+                    bool check_timestamp);
+
+ private:
+  CertInfoList cert_list_;
+};
+
+
+// ExtractAllCertificatesFromSignature() takes in a signed file, extracts all
+// the certificates related to its signature and returns them in a CertList
+// object.
+void ExtractAllCertificatesFromSignature(const wchar_t* signed_file,
+                                         CertList* cert_list);
+
+// TODO(omaha): the time check verification algorithm below seems weak if not
+// completely wrong. The time stamp check should be done against the time
+// stamp signature instead of the current time. For example, modifying the
+// system time allows the test to pass or fail. See the documentation for
+// WinVerifyTrust and WTD_LIFETIME_SIGNING_FLAG.
+//
+// VerifySigneeIsGoogle() tries to verify the signee is Google by matching
+// the subject name on the certificate to "Google Inc" and its corresponding
+// dept name on the certificate to "Engineering". If matched, this returns true
+// otherwise it returns false. The time validity of the certicate is checked.
+bool VerifySigneeIsGoogle(const wchar_t* signed_file);
+
+// Verifies the signee but not the time stamp of the certificate.
+bool VerifySigneeIsGoogleNoTimestampCheck(const wchar_t* signed_file);
+
+// Returns S_OK if a given signed file contains a signature
+// that could be successfully verified using one of the trust providers
+// IE relies on. This means that, whoever signed the file, they should've signed
+// using certificate issued by a well-known (to IE) trust provider like
+// Verisign, Inc.
+HRESULT VerifySignature(const wchar_t* signed_file, bool allow_network_check);
+
+// Returns true if a given signed file contains a valid signature.
+inline bool SignatureIsValid(const wchar_t* signed_file,
+                             bool allow_network_check) {
+  return VerifySignature(signed_file, allow_network_check) == S_OK;
+}
+
+// Gets the timestamp for the file's signature.
+HRESULT GetSigningTime(const wchar_t* signed_file, SYSTEMTIME* signing_time);
+
+// Verifies that the file was signed within the specified number of days.
+HRESULT VerifyFileSignedWithinDays(const wchar_t* signed_file, int days);
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SIGNATUREVALIDATOR_H__
diff --git a/common/signaturevalidator_unittest.cc b/common/signaturevalidator_unittest.cc
new file mode 100644
index 0000000..117cb03
--- /dev/null
+++ b/common/signaturevalidator_unittest.cc
@@ -0,0 +1,58 @@
+// 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/third_party/gtest/include/gtest/gtest.h"
+
+namespace omaha {
+
+class SignatureValidatorTest : public testing::Test {
+  virtual void SetUp() {
+  }
+
+  virtual void TearDown() {
+  }
+};
+
+
+TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_OfficiallySigned) {
+  const TCHAR kUnsignedExecutable[] =
+      _T("unittest_support\\SaveArguments.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kUnsignedExecutable));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_TRUE(VerifySigneeIsGoogleNoTimestampCheck(executable_full_path));
+}
+
+TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_OmahaTestSigned) {
+  const TCHAR kUnsignedExecutable[] =
+      _T("unittest_support\\SaveArguments_OmahaTestSigned.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kUnsignedExecutable));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_TRUE(VerifySigneeIsGoogleNoTimestampCheck(executable_full_path));
+}
+
+}  // namespace omaha
diff --git a/common/single_instance.cc b/common/single_instance.cc
new file mode 100644
index 0000000..fea4c06
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..df0d2d0
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..3e99f89
--- /dev/null
+++ b/common/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/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/smart_handle.h b/common/smart_handle.h
new file mode 100644
index 0000000..154fe04
--- /dev/null
+++ b/common/smart_handle.h
@@ -0,0 +1,240 @@
+// 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.
+// ========================================================================
+//
+// Classes for automatically closing handles.
+
+#ifndef OMAHA_COMMON_SMART_HANDLE_H_
+#define OMAHA_COMMON_SMART_HANDLE_H_
+
+#include <wincrypt.h>
+
+namespace omaha {
+
+/**
+* Base traits class for handles.
+* This base class provides default implementation for InvalidValue and IsValid
+* @param T  The handle type to be wrapped.
+*/
+template<class T>
+class BaseHandleTraitsT {
+ public:
+  // Typedef that is used by this class and derived classes
+  typedef T HandleType;
+
+  // Returns the invalid handle value
+  static HandleType InvalidValue() {
+    return NULL;
+  }
+
+  // Returns true only if the given handle h is invalid
+  static bool IsValid(const HandleType& h) {
+    return h != InvalidValue();
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(BaseHandleTraitsT);
+};
+
+/**
+* Smart handle class.
+* Offers basic HANDLE functionality such as cast, attach/detach and automatic Close().
+*/
+template<class T, class Traits, class AlternateType = T>
+class HandleT {
+ public:
+  // Default constructor.
+  HandleT() : h_(Traits::InvalidValue()) {
+  }
+
+  // Constructor that assumes ownership of the supplied handle
+  explicit HandleT(T h) : h_(h) {
+  }
+
+  // Destructor calls @ref Close()
+  ~HandleT() {
+    Close();
+  }
+
+  // Assumes ownership of the supplied handle,
+  // potentially closing an already held handle.
+  void Attach(T h) {
+    Close();
+    h_ = h;
+  }
+
+  // Transfers ownership to the caller and sets the internal
+  // state to InvalidValue().
+  T Detach() {
+    T h = h_;
+    h_ = Traits::InvalidValue();
+    return h;
+  }
+
+  // Handle accessor
+  T handle() {
+    return h_;
+  }
+
+  // An alternate cast for the handle.
+  // This can be useful for GDI objects that are used
+  // in functions that e.g. accept both HGDIOBJ and HBITMAP.
+  AlternateType alt_type() {
+    return reinterpret_cast<AlternateType>(h_);
+  }
+
+  // Accesses the contained handle
+  operator T() {
+    return h_;
+  }
+
+  T& receive() {
+    ASSERT(!IsValid(), (L"Should only be used for out arguments"));
+    return h_;
+  }
+
+  // @returns true only if the handle is valid as depicted
+  //  by the traits class.
+  bool IsValid() {
+    return Traits::IsValid(h_);
+  }
+
+  // Closes the handle
+  void Close() {
+    if (Traits::IsValid(h_)) {
+      Traits::Close(h_);
+      h_ = Traits::InvalidValue();
+    }
+  }
+
+ protected:
+  T h_;
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(HandleT);
+};
+
+
+/*
+* Traits class for a regular Win32 HANDLE.
+*/
+class HandleTraitsWin32Handle : public BaseHandleTraitsT<HANDLE> {
+ public:
+  // Calls FindClose to close the handle.
+  static bool Close(HandleType h) {
+    return (::CloseHandle(h) != false);
+  }
+
+  // Returns the invalid handle value
+  static HandleType InvalidValue() {
+    return NULL;  // note that INVALID_HANDLE_VALUE is also an invalid handle
+  }
+
+  // Returns true only if the given handle h is invalid
+  static bool IsValid(const HandleType& h) {
+    return h != InvalidValue() && h != INVALID_HANDLE_VALUE;
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsWin32Handle);
+};
+
+/*
+* Traits class for FindXXXFile handles.
+*/
+class HandleTraitsFindHandle : public BaseHandleTraitsT<HANDLE> {
+ public:
+  // Calls FindClose to close the handle.
+  static bool Close(HandleType h) {
+    return (::FindClose(h) != false);
+  }
+
+  // Returns the invalid handle value
+  static HandleType InvalidValue() {
+    return INVALID_HANDLE_VALUE;
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsFindHandle);
+};
+
+/*
+* Traits for an HMENU.
+*/
+class HandleTraitsHMenu : public BaseHandleTraitsT<HMENU> {
+ public:
+  // Calls DestroyMenu to destroy the menu.
+  static bool Close(HandleType h) {
+    return (::DestroyMenu(h) != FALSE);
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsHMenu);
+};
+
+/*
+* Traits for an HCRYPTKEY.
+*/
+class HandleTraitsHCryptKey : public BaseHandleTraitsT<HCRYPTKEY> {
+ public:
+  static bool Close(HandleType h) {
+    return (::CryptDestroyKey(h) != FALSE);
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsHCryptKey);
+};
+
+/*
+* Traits for an HCRYPTHASH.
+*/
+class HandleTraitsHCryptHash : public BaseHandleTraitsT<HCRYPTHASH> {
+ public:
+  static bool Close(HandleType h) {
+    return (::CryptDestroyHash(h) != FALSE);
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsHCryptHash);
+};
+
+/*
+ * Traits for LoadLibrary/FreeLibrary.
+ */
+class HandleTraitsLibrary : public BaseHandleTraitsT<HMODULE> {
+ public:
+  static bool Close(HandleType h) {
+    return (::FreeLibrary(h) != FALSE);
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsLibrary);
+};
+
+
+/*
+* Win32 handle types.  Add new ones here as you need them.
+* Note that GDI handle types should be kept in common/gdi_smart_ptr.h
+* rather than here.
+*/
+typedef HandleT<HANDLE, HandleTraitsWin32Handle> AutoHandle;
+typedef HandleT<HANDLE, HandleTraitsFindHandle> AutoFindHandle;
+typedef HandleT<HMENU, HandleTraitsHMenu> AutoHMenu;
+typedef HandleT<HCRYPTHASH, HandleTraitsHCryptHash> AutoHCryptHash;
+typedef HandleT<HCRYPTKEY, HandleTraitsHCryptKey> AutoHCryptKey;
+typedef HandleT<HINSTANCE, HandleTraitsLibrary> AutoLibrary;
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SMART_HANDLE_H_
diff --git a/common/sta.cc b/common/sta.cc
new file mode 100644
index 0000000..cb3c59f
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..76204d0
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..682b66f
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..1e52b68
--- /dev/null
+++ b/common/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/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/static_assert.h b/common/static_assert.h
new file mode 100644
index 0000000..b494742
--- /dev/null
+++ b/common/static_assert.h
@@ -0,0 +1,45 @@
+// 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.
+// ========================================================================
+//
+// static_assert.h
+//
+// Compile-time asserts
+//
+// Based on the one from boost:
+// http://www.boost.org/boost/static_assert.hpp
+//
+// Usage:
+//
+// kStaticAssert(constant_boolean_expression);
+//
+// This can appear at file scope or within a block (anyplace a typedef
+// is OK).
+//
+// TODO(omaha): deprecate and replace with the static assert in base/basictypes
+#ifndef OMAHA_COMMON_STATIC_ASSERT_H_
+#define OMAHA_COMMON_STATIC_ASSERT_H_
+
+template <bool> struct STATIC_ASSERTION_FAILURE;
+
+template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
+
+template<int> struct static_assert_test{};
+
+#define STATIC_ASSERT( B ) \
+typedef static_assert_test<\
+  sizeof(STATIC_ASSERTION_FAILURE< (bool)( B ) >)>\
+    static_assert_typedef_ ##  __LINE__
+
+#endif  // OMAHA_COMMON_STATIC_ASSERT_H_
diff --git a/common/store_watcher.h b/common/store_watcher.h
new file mode 100644
index 0000000..d630540
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..9e9b63d
--- /dev/null
+++ b/common/string.cc
@@ -0,0 +1,3345 @@
+// 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
new file mode 100644
index 0000000..258aa1b
--- /dev/null
+++ b/common/string.h
@@ -0,0 +1,537 @@
+// 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
new file mode 100644
index 0000000..01d838b
--- /dev/null
+++ b/common/string_unittest.cc
@@ -0,0 +1,1712 @@
+// 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
new file mode 100644
index 0000000..926231e
--- /dev/null
+++ b/common/synchronized.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.
+// ========================================================================
+//
+// 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
new file mode 100644
index 0000000..b280845
--- /dev/null
+++ b/common/synchronized.h
@@ -0,0 +1,355 @@
+// 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
new file mode 100644
index 0000000..41095f8
--- /dev/null
+++ b/common/system.cc
@@ -0,0 +1,1033 @@
+// 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
new file mode 100644
index 0000000..017b042
--- /dev/null
+++ b/common/system.h
@@ -0,0 +1,223 @@
+// 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
new file mode 100644
index 0000000..830c94a
--- /dev/null
+++ b/common/system_info.cc
@@ -0,0 +1,462 @@
+// 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
new file mode 100644
index 0000000..2acba56
--- /dev/null
+++ b/common/system_info.h
@@ -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.
+// ========================================================================
+
+// 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
new file mode 100644
index 0000000..cadfe18
--- /dev/null
+++ b/common/system_info_unittest.cc
@@ -0,0 +1,48 @@
+// 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
new file mode 100644
index 0000000..010f49c
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..f7e743b
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..d2b149c
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..c0ae30b
--- /dev/null
+++ b/common/thread_pool.cc
@@ -0,0 +1,124 @@
+// 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
new file mode 100644
index 0000000..abbdf28
--- /dev/null
+++ b/common/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_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
new file mode 100644
index 0000000..70deeca
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..ec17dbc
--- /dev/null
+++ b/common/time.cc
@@ -0,0 +1,500 @@
+// 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
new file mode 100644
index 0000000..7b704da
--- /dev/null
+++ b/common/time.h
@@ -0,0 +1,146 @@
+// 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
new file mode 100644
index 0000000..04318b6
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..9f7690d
--- /dev/null
+++ b/common/timer.cc
@@ -0,0 +1,214 @@
+// 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
new file mode 100644
index 0000000..0ec6bee
--- /dev/null
+++ b/common/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_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
new file mode 100644
index 0000000..01905ed
--- /dev/null
+++ b/common/timer_unittest.cc
@@ -0,0 +1,232 @@
+// 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
new file mode 100644
index 0000000..2a532ad
--- /dev/null
+++ b/common/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 "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.h b/common/tr_rand.h
new file mode 100644
index 0000000..e7469f5
--- /dev/null
+++ b/common/tr_rand.h
@@ -0,0 +1,35 @@
+// 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).
+//
+
+#ifndef OMAHA_COMMON_TR_RAND_H_
+#define OMAHA_COMMON_TR_RAND_H_
+
+namespace omaha {
+
+#define kMaximumRandomValue 65535
+
+// Same prototypes as CRT srand/rand
+void tr_srand(unsigned int seed);
+int tr_rand();
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_TR_RAND_H_
diff --git a/common/tr_rand_unittest.cc b/common/tr_rand_unittest.cc
new file mode 100644
index 0000000..682d254
--- /dev/null
+++ b/common/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/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/type_utils.h b/common/type_utils.h
new file mode 100644
index 0000000..edbd286
--- /dev/null
+++ b/common/type_utils.h
@@ -0,0 +1,68 @@
+// 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_TYPE_UTILS_H_
+#define OMAHA_COMMON_TYPE_UTILS_H_
+
+namespace omaha {
+
+//
+// Detecting convertibility and inheritence at compile time
+// (Extracted from: Modern C++ Design)
+//
+
+// Evaluates true if U inherites from T publically, or if T and U are same type
+#define SUPERSUBCLASS(T, U) \
+  (ConversionUtil<const U*, const T*>::exists && \
+  !ConversionUtil<const T*, const void*>::same_type)
+
+// Evaluates true only if U inherites from T publically
+#define SUPERSUBCLASS_STRICT(T, U) \
+  (SUPERSUBCLASS(T, U) && \
+  !ConversionUtil<const T, const U>::same_type)
+
+// Perform type test
+template <class T, class U>
+class ConversionUtil {
+ private:
+  typedef char Small;
+  class Big {
+    char dummy[2];
+  };
+  static Small Test(U);
+  static Big Test(...);
+  static T MakeT();
+
+ public:
+  // Tell whether there is ConversionUtil from T to U
+  enum { exists = sizeof(Test(MakeT())) == sizeof(Small) };
+
+  // Tells whether there are ConversionUtils between T and U in both directions
+  enum { exists_2way = exists && ConversionUtil<U, T>::exists };
+
+  // Tells whether there are same type
+  enum { same_type = false };
+};
+
+// Perform same type test through partial template specialization
+template<class T>
+class ConversionUtil<T, T> {
+ public:
+  enum { exists = 1, exists_2way = 1, same_type = 1 };
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_TYPE_UTILS_H_
diff --git a/common/unittest_utils.cc b/common/unittest_utils.cc
new file mode 100644
index 0000000..a9927ab
--- /dev/null
+++ b/common/unittest_utils.cc
@@ -0,0 +1,124 @@
+// 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
new file mode 100644
index 0000000..a7fc15c
--- /dev/null
+++ b/common/unittest_utils.h
@@ -0,0 +1,62 @@
+// 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/user_info.cc b/common/user_info.cc
new file mode 100644
index 0000000..486ef27
--- /dev/null
+++ b/common/user_info.cc
@@ -0,0 +1,87 @@
+// 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_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) {
+  CAccessToken token;
+  CSid current_sid;
+  if (!token.GetProcessToken(TOKEN_QUERY) || !token.GetUser(&current_sid)) {
+    HRESULT hr = HRESULTFromLastError();
+    ASSERT(false, (_T("[Failed to get current user sid[0x%x]"), 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 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
new file mode 100644
index 0000000..1df0427
--- /dev/null
+++ b/common/user_info.h
@@ -0,0 +1,49 @@
+// 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.
+// ========================================================================
+//
+// 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 <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 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
new file mode 100644
index 0000000..14588ad
--- /dev/null
+++ b/common/user_info_unittest.cc
@@ -0,0 +1,50 @@
+// 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/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 {
+
+TEST(UserInfoTest, GetCurrentUser) {
+  CString name, domain, sid;
+  ASSERT_HRESULT_SUCCEEDED(user_info::GetCurrentUser(&name, &domain, &sid));
+}
+
+TEST(UserInfoTest, IsLocalSystemUser) {
+  bool is_system = false;
+  CString sid;
+  ASSERT_HRESULT_SUCCEEDED(user_info::IsLocalSystemUser(&is_system, &sid));
+}
+
+TEST(UserInfoTest, GetCurrentUserSid) {
+  CString name, domain, sid1;
+  ASSERT_HRESULT_SUCCEEDED(user_info::GetCurrentUser(&name, &domain, &sid1));
+  CString sid2;
+  ASSERT_HRESULT_SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &sid2));
+  ASSERT_STREQ(sid1, sid2);
+}
+
+// Expect the unit tests do not run impersonated.
+TEST(UserInfoTest, GetCurrentThreadUser) {
+  CString thread_sid;
+  ASSERT_EQ(HRESULT_FROM_WIN32(ERROR_NO_TOKEN),
+            user_info::GetCurrentThreadUser(&thread_sid));
+}
+
+}  // namespace omaha
+
diff --git a/common/user_rights.cc b/common/user_rights.cc
new file mode 100644
index 0000000..cf10673
--- /dev/null
+++ b/common/user_rights.cc
@@ -0,0 +1,195 @@
+// 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
new file mode 100644
index 0000000..24aa011
--- /dev/null
+++ b/common/user_rights.h
@@ -0,0 +1,72 @@
+// 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
new file mode 100644
index 0000000..f62374a
--- /dev/null
+++ b/common/user_rights_unittest.cc
@@ -0,0 +1,29 @@
+// 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
new file mode 100644
index 0000000..9dd25c5
--- /dev/null
+++ b/common/utils.cc
@@ -0,0 +1,1927 @@
+// 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/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
new file mode 100644
index 0000000..1507f98
--- /dev/null
+++ b/common/utils.h
@@ -0,0 +1,811 @@
+// 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_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., at an OEM factory).
+bool IsWindowsInstalling();
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_UTILS_H__
+
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
new file mode 100644
index 0000000..b166c5e
--- /dev/null
+++ b/common/utils_unittest.cc
@@ -0,0 +1,589 @@
+// 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 <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/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.AppendFormat(_T("%s\\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));
+}
+
+TEST(UtilsTest, GetEnvironmentVariableAsString) {
+  EXPECT_STREQ(_T("C:"), GetEnvironmentVariableAsString(_T("SystemDrive")));
+  EXPECT_STREQ(_T("GOOGLE"), GetEnvironmentVariableAsString(_T("USERDOMAIN")));
+  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
new file mode 100644
index 0000000..f052983
--- /dev/null
+++ b/common/vista_utils.cc
@@ -0,0 +1,482 @@
+// 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
new file mode 100644
index 0000000..8592d32
--- /dev/null
+++ b/common/vista_utils.h
@@ -0,0 +1,101 @@
+// 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
new file mode 100644
index 0000000..06230cb
--- /dev/null
+++ b/common/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/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
new file mode 100644
index 0000000..0d4bc6b
--- /dev/null
+++ b/common/vistautil.cc
@@ -0,0 +1,512 @@
+// 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/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;
+}
+
+bool IsUserRunningSplitToken() {
+  if (!IsVistaOrLater()) {
+    return false;
+  }
+
+  scoped_handle process_token;
+  if (!::OpenProcessToken(GetCurrentProcess(),
+                          TOKEN_QUERY,
+                          address(process_token))) {
+    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+    UTIL_LOG(L1, (_T("[OpenProcessToken failed][0x%x]"), hr));
+    return false;
+  }
+
+  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 = HRESULT_FROM_WIN32(::GetLastError());
+    UTIL_LOG(L1, (_T("[GetTokenInformation failed][0x%x]"), hr));
+    return false;
+  }
+
+  return (elevation_type == TokenElevationTypeFull ||
+          elevation_type == TokenElevationTypeLimited);
+}
+
+bool IsUACDisabled() {
+  if (!IsVistaOrLater()) {
+    return false;
+  }
+
+  if (IsUserRunningSplitToken()) {
+    // Split token indicates that UAC is on.
+    return false;
+  }
+
+  const TCHAR* key_name = _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\")
+                          _T("CurrentVersion\\Policies\\System");
+
+  DWORD val = 1;    // Assume UAC is enabled.
+  return SUCCEEDED(RegKey::GetValue(key_name, _T("EnableLUA"), &val)) && !val;
+}
+
+
+HRESULT RunElevated(const TCHAR* file_path,
+                    const TCHAR* parameters,
+                    int show_window,
+                    DWORD* exit_code) {
+  OPT_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);
+}
+
+}  // namespace vista_util
+
+}  // namespace omaha
+
diff --git a/common/vistautil.h b/common/vistautil.h
new file mode 100644
index 0000000..0a7a3cc
--- /dev/null
+++ b/common/vistautil.h
@@ -0,0 +1,131 @@
+// 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.
+#define MEDIUM_INTEGRITY_SDDL_SACL _T("S:(ML;;NW;;;ME)")
+
+// The LABEL_SECURITY_INFORMATION SDDL SACL for low integrity.
+#define LOW_INTEGRITY_SDDL_SACL    _T("S:(ML;;NW;;;LW)")
+
+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.
+bool IsUserRunningSplitToken();
+
+// 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);
+
+}  // namespace vista_util
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_VISTAUTIL_H__
+
diff --git a/common/vistautil_unittest.cc b/common/vistautil_unittest.cc
new file mode 100644
index 0000000..17934d8
--- /dev/null
+++ b/common/vistautil_unittest.cc
@@ -0,0 +1,41 @@
+// 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/window_utils.cc b/common/window_utils.cc
new file mode 100644
index 0000000..1a97ac2
--- /dev/null
+++ b/common/window_utils.cc
@@ -0,0 +1,112 @@
+// 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
new file mode 100644
index 0000000..4f95ca8
--- /dev/null
+++ b/common/window_utils.h
@@ -0,0 +1,55 @@
+// 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
new file mode 100644
index 0000000..64c5f62
--- /dev/null
+++ b/common/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/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.h b/common/wmi_query.h
new file mode 100644
index 0000000..c631056
--- /dev/null
+++ b/common/wmi_query.h
@@ -0,0 +1,65 @@
+// 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_WMI_QUERY_H_
+#define OMAHA_COMMON_WMI_QUERY_H_
+
+#include <windows.h>
+#include <wbemidl.h>
+#include <atlbase.h>
+#include <atlcomcli.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class WmiQuery {
+ public:
+  WmiQuery();
+  ~WmiQuery();
+
+  // Connects to the server to get WMI service.
+  HRESULT Connect(const TCHAR* resource);
+
+  // Queries the service.
+  HRESULT Query(const TCHAR* query);
+
+  // Reads the next row.
+  HRESULT Next();
+
+  // Returns true at the end.
+  bool AtEnd();
+
+  // Gets the value of the named property.
+  HRESULT GetValue(const TCHAR* name, CComVariant* value);
+  HRESULT GetValue(const TCHAR* name, CString* value);
+  HRESULT GetValue(const TCHAR* name, bool* value);
+  HRESULT GetValue(const TCHAR* name, int* value);
+  HRESULT GetValue(const TCHAR* name, uint32* value);
+
+ private:
+  CComPtr<IWbemLocator> wbem_;
+  CComPtr<IWbemServices> service_;
+  CComPtr<IEnumWbemClassObject> enumerator_;
+  CComPtr<IWbemClassObject> obj_;
+  bool at_end_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(WmiQuery);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_WMI_QUERY_H_
+
diff --git a/common/wmi_query_unittest.cc b/common/wmi_query_unittest.cc
new file mode 100644
index 0000000..83aaa4a
--- /dev/null
+++ b/common/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/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_utils.cc b/common/xml_utils.cc
new file mode 100644
index 0000000..4320da9
--- /dev/null
+++ b/common/xml_utils.cc
@@ -0,0 +1,602 @@
+// 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;
+  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 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
new file mode 100644
index 0000000..8ccb397
--- /dev/null
+++ b/common/xml_utils.h
@@ -0,0 +1,221 @@
+// 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);
+
+// 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
new file mode 100644
index 0000000..2d66c78
--- /dev/null
+++ b/common/xml_utils_unittest.cc
@@ -0,0 +1,120 @@
+// 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.AppendFormat(_T("%s\\%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 to memory.
+  // The input must be Unicode but our test file is UTF-8 - so read
+  // it and convert it to Unicode.
+  std::vector<byte> buffer_utf8;
+  ASSERT_SUCCEEDED(ReadEntireFile(temp_file, 0, &buffer_utf8));
+  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.
+  CComPtr<IXMLDOMDocument> xmldoc2;
+  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
new file mode 100644
index 0000000..d22364f
--- /dev/null
+++ b/core/build.scons
@@ -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.
+# ========================================================================
+
+Import('env')
+
+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/',
+
+    # 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',
+    ]
+
+local_env.ComponentLibrary('core', inputs)
diff --git a/core/core.cc b/core/core.cc
new file mode 100644
index 0000000..0e0c726
--- /dev/null
+++ b/core/core.cc
@@ -0,0 +1,441 @@
+// 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.
+// ========================================================================
+
+// Core is the long-lived Omaha process. It runs one instance for the
+// machine and one instance for each user session, including console and TS
+// sessions.
+// If the same user is logged in multiple times, only one core process will
+// be running.
+
+#include "omaha/core/core.h"
+#include <atlsecurity.h>
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+#include "omaha/common/app_util.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/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/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"
+
+namespace omaha {
+
+Core::Core()
+    : is_system_(false),
+      is_crash_handler_enabled_(false),
+      main_thread_id_(0) {
+  CORE_LOG(L1, (_T("[Core::Core]")));
+}
+
+Core::~Core() {
+  CORE_LOG(L1, (_T("[Core::~Core]")));
+  scheduler_.reset(NULL);
+  system_monitor_.reset(NULL);
+}
+
+// We always return S_OK, because the core can be invoked from the system
+// scheduler, and the scheduler does not work well if the process returns
+// an error. We do not depend on the return values from the Core elsewhere.
+HRESULT Core::Main(bool is_system, bool is_crash_handler_enabled) {
+  HRESULT hr = DoMain(is_system, is_crash_handler_enabled);
+  if (FAILED(hr)) {
+    OPT_LOG(LW, (_T("[Core::DoMain failed][0x%x]"), hr));
+  }
+
+  return S_OK;
+}
+
+HRESULT Core::DoMain(bool is_system, bool is_crash_handler_enabled) {
+  main_thread_id_ = ::GetCurrentThreadId();
+  is_system_ = is_system;
+  is_crash_handler_enabled_ = is_crash_handler_enabled;
+
+  if (ConfigManager::Instance()->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.
+    OPT_LOG(L1, (_T("[Exiting because an OEM is installing Windows]")));
+    ASSERT1(is_system_);
+    return S_OK;
+  }
+
+  // Do a code red check as soon as possible.
+  StartCodeRed();
+
+  CORE_LOG(L2, (_T("[IsGoogler %d]"), ConfigManager::Instance()->IsGoogler()));
+
+  NamedObjectAttributes 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]")));
+    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));
+  }
+
+  // TODO(Omaha): Delay starting update worker when run at startup.
+  StartUpdateWorker();
+
+  // Start the crash handler if necessary.
+  if (is_crash_handler_enabled_) {
+    HRESULT hr = StartCrashHandler();
+    if (FAILED(hr)) {
+      OPT_LOG(LW, (_T("[Failed to start crash handler][0x%08x]"), hr));
+    }
+  }
+
+  // The scheduled task will start the Update Worker at intervals. If the task
+  // is not installed, then Omaha uses the built-in scheduler hosted by the core
+  // and it keeps the core running. In addition, for the machine GoogleUpdate,
+  // if the service is not installed, then Omaha uses the elevator interface
+  // hosted by the core, and this keeps the core running.
+  if (goopdate_utils::IsInstalledGoopdateTaskCore(is_system_)) {
+    if (!is_system_) {
+      return S_OK;
+    }
+
+    if (goopdate_utils::IsServiceInstalled()) {
+      return S_OK;
+    }
+    ++metric_core_run_service_missing;
+  } else {
+    ++metric_core_run_scheduled_task_missing;
+  }
+
+  // Force the main thread to create a message queue so any future WM_QUIT
+  // message posted by the ShutdownHandler will be received. If the main
+  // thread does not have a message queue, the message can be lost.
+  MSG msg = {0};
+  ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+  reactor_.reset(new Reactor);
+  shutdown_handler_.reset(new ShutdownHandler);
+  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)) {
+    return hr;
+  }
+
+  system_monitor_.reset(new SystemMonitor(is_system_));
+  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;
+}
+
+// Signals the core to shutdown. The shutdown method is called by a thread
+// running in the thread pool. It posts a WM_QUIT to the main thread, which
+// causes it to break out of the message loop. If the message can't be posted,
+// it terminates the process unconditionally.
+HRESULT Core::Shutdown() {
+  OPT_LOG(L1, (_T("[Google Update is shutting down...]")));
+  ASSERT1(::GetCurrentThreadId() != main_thread_id_);
+  if (::PostThreadMessage(main_thread_id_, WM_QUIT, 0, 0)) {
+    return S_OK;
+  }
+
+  ASSERT(false, (_T("Failed to post WM_QUIT")));
+  uint32 exit_code = static_cast<uint32>(E_ABORT);
+  VERIFY1(::TerminateProcess(::GetCurrentProcess(), exit_code));
+  return S_OK;
+}
+
+void Core::LastCheckedDeleted() {
+  OPT_LOG(L1, (_T("[Core::LastCheckedDeleted]")));
+  VERIFY1(SUCCEEDED(StartUpdateWorker()));
+}
+
+void Core::NoRegisteredClients() {
+  OPT_LOG(L1, (_T("[Core::NoRegisteredClients]")));
+  VERIFY1(SUCCEEDED(StartUpdateWorker()));
+}
+
+HRESULT Core::DoRun() {
+  OPT_LOG(L1, (_T("[Core::DoRun]")));
+
+  // Trim the process working set to minimum. It does not need a more complex
+  // algorithm for now. Likely the working set will increase slightly over time
+  // as the core is handling events.
+  VERIFY1(::SetProcessWorkingSetSize(::GetCurrentProcess(),
+                                     static_cast<uint32>(-1),
+                                     static_cast<uint32>(-1)));
+  return DoHandleEvents();
+}
+
+HRESULT Core::DoHandleEvents() {
+  CORE_LOG(L1, (_T("[Core::DoHandleEvents]")));
+  MSG msg = {0};
+  int result = 0;
+  while ((result = ::GetMessage(&msg, 0, 0, 0)) != 0) {
+    ::DispatchMessage(&msg);
+    if (result == -1) {
+      break;
+    }
+  }
+  CORE_LOG(L3, (_T("[GetMessage returned %d]"), result));
+  return (result != -1) ? S_OK : HRESULTFromLastError();
+}
+
+HRESULT Core::StartUpdateWorker() 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));
+
+  CString exe_path = goopdate_utils::BuildGoogleUpdateExePath(is_system_);
+  CommandLineBuilder builder(COMMANDLINE_MODE_UA);
+  builder.set_is_uninstall_set(is_uninstall);
+  CString cmd_line = builder.GetCommandLineArgs();
+  HRESULT hr = System::StartProcessWithArgs(exe_path, cmd_line);
+  if (SUCCEEDED(hr)) {
+    ++metric_core_worker_succeeded;
+  } else {
+    CORE_LOG(LE, (_T("[can't start update worker][0x%08x]"), hr));
+  }
+  ++metric_core_worker_total;
+  return hr;
+}
+
+HRESULT Core::StartCodeRed() const {
+  if (RegKey::HasValue(MACHINE_REG_UPDATE_DEV, kRegValueNoCodeRedCheck)) {
+    CORE_LOG(LW, (_T("[Code Red is disabled for this system]")));
+    return E_ABORT;
+  }
+
+  CORE_LOG(L2, (_T("[Core::StartCodeRed]")));
+
+  CString exe_path = goopdate_utils::BuildGoogleUpdateExePath(is_system_);
+  CommandLineBuilder builder(COMMANDLINE_MODE_CODE_RED_CHECK);
+  CString cmd_line = builder.GetCommandLineArgs();
+  HRESULT hr = System::StartProcessWithArgs(exe_path, cmd_line);
+  if (SUCCEEDED(hr)) {
+    ++metric_core_cr_succeeded;
+  } else {
+    CORE_LOG(LE, (_T("[can't start Code Red worker][0x%08x]"), hr));
+  }
+  ++metric_core_cr_total;
+  return hr;
+}
+
+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);
+  if (SUCCEEDED(hr)) {
+    ++metric_core_start_crash_handler_succeeded;
+  } else {
+    CORE_LOG(LE, (_T("[can't 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();
+  VERIFY1(SUCCEEDED(omaha::AggregateMetrics(is_system_)));
+}
+
+// Collects: working set, peak working set, handle count, process uptime,
+// user disk free space on the current drive, process kernel time, and process
+// user time.
+void Core::CollectMetrics() const {
+  uint64 working_set(0), peak_working_set(0);
+  VERIFY1(SUCCEEDED(System::GetProcessMemoryStatistics(&working_set,
+                                                       &peak_working_set,
+                                                       NULL,
+                                                       NULL)));
+  metric_core_working_set      = working_set;
+  metric_core_peak_working_set = peak_working_set;
+
+  metric_core_handle_count = System::GetProcessHandleCount();
+
+  FILETIME now = {0};
+  FILETIME creation_time = {0};
+  FILETIME exit_time = {0};
+  FILETIME kernel_time = {0};
+  FILETIME user_time = {0};
+
+  ::GetSystemTimeAsFileTime(&now);
+
+  VERIFY1(::GetProcessTimes(::GetCurrentProcess(),
+                            &creation_time,
+                            &exit_time,
+                            &kernel_time,
+                            &user_time));
+
+  ASSERT1(FileTimeToInt64(now) >= FileTimeToInt64(creation_time));
+  uint64 uptime_100ns = FileTimeToInt64(now) - FileTimeToInt64(creation_time);
+
+  metric_core_uptime_ms      = uptime_100ns / kMillisecsTo100ns;
+  metric_core_kernel_time_ms = FileTimeToInt64(kernel_time) / kMillisecsTo100ns;
+  metric_core_user_time_ms   = FileTimeToInt64(user_time) / kMillisecsTo100ns;
+
+  uint64 free_bytes_current_user(0);
+  uint64 total_bytes_current_user(0);
+  uint64 free_bytes_all_users(0);
+
+  CString directory_name(app_util::GetCurrentModuleDirectory());
+  VERIFY1(SUCCEEDED(System::GetDiskStatistics(directory_name,
+                                              &free_bytes_current_user,
+                                              &total_bytes_current_user,
+                                              &free_bytes_all_users)));
+  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
new file mode 100644
index 0000000..32950fc
--- /dev/null
+++ b/core/core.h
@@ -0,0 +1,116 @@
+// 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.
+// ========================================================================
+
+
+// Core uses a reactor design pattern to register event handlers for kernel
+// events, demultiplex them, and transfer control to the respective event
+// handlers.
+
+#ifndef OMAHA_CORE_CORE_H_
+#define OMAHA_CORE_CORE_H_
+
+#include <atlbase.h>
+#include <atlsecurity.h>
+#include <atlstr.h>
+#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/core/google_update_core.h"
+#include "omaha/core/system_monitor.h"
+
+namespace omaha {
+
+class Reactor;
+class Scheduler;
+class ShutdownHandler;
+class LegacyManifestHandler;
+
+class Core
+    : public ShutdownCallback,
+      public SystemMonitorObserver {
+ public:
+  Core();
+  virtual ~Core();
+
+  // Executes the instance entry point with given parameters.
+  HRESULT Main(bool is_system, bool is_crash_handler_enabled);
+
+  // Starts an update worker process.
+  HRESULT StartUpdateWorker() const;
+
+  // Starts a code red process.
+  HRESULT StartCodeRed() const;
+
+  // Starts an install worker process.
+  HRESULT StartInstallWorker();
+
+  // Starts the crash handler.
+  HRESULT StartCrashHandler() const;
+
+  // Aggregates the core metrics.
+  void AggregateMetrics() const;
+
+  Reactor* reactor() const { return reactor_.get(); }
+  bool is_system() const { return is_system_; }
+
+ private:
+
+  HRESULT DoMain(bool is_system, bool is_crash_handler_enabled);
+
+  // ShutdownCallback interface.
+  // Signals the core to stop handling events and exit.
+  virtual HRESULT Shutdown();
+
+  // SystemMonitorObserver interface.
+  virtual void LastCheckedDeleted();
+  virtual void NoRegisteredClients();
+
+  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;
+
+  bool is_system_;
+
+  // True if the core has to kickoff the crash handler.
+  bool is_crash_handler_enabled_;
+
+  DWORD main_thread_id_;        // The id of the thread that runs Core::Main.
+
+  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_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Core);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_CORE_CORE_H_
+
diff --git a/core/core_metrics.cc b/core/core_metrics.cc
new file mode 100644
index 0000000..382aafd
--- /dev/null
+++ b/core/core_metrics.cc
@@ -0,0 +1,45 @@
+// 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/core_metrics.h"
+
+namespace omaha {
+
+DEFINE_METRIC_integer(core_working_set);
+DEFINE_METRIC_integer(core_peak_working_set);
+
+DEFINE_METRIC_integer(core_handle_count);
+
+DEFINE_METRIC_integer(core_uptime_ms);
+DEFINE_METRIC_integer(core_kernel_time_ms);
+DEFINE_METRIC_integer(core_user_time_ms);
+
+DEFINE_METRIC_integer(core_disk_space_available);
+
+DEFINE_METRIC_count(core_worker_total);
+DEFINE_METRIC_count(core_worker_succeeded);
+DEFINE_METRIC_count(core_cr_total);
+DEFINE_METRIC_count(core_cr_succeeded);
+
+DEFINE_METRIC_integer(core_cr_expected_timer_interval_ms);
+DEFINE_METRIC_integer(core_cr_actual_timer_interval_ms);
+
+DEFINE_METRIC_count(core_start_crash_handler_total);
+DEFINE_METRIC_count(core_start_crash_handler_succeeded);
+
+DEFINE_METRIC_count(core_run_scheduled_task_missing);
+DEFINE_METRIC_count(core_run_service_missing);
+}  // namespace omaha
+
diff --git a/core/core_metrics.h b/core/core_metrics.h
new file mode 100644
index 0000000..a63cfcc
--- /dev/null
+++ b/core/core_metrics.h
@@ -0,0 +1,63 @@
+// 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 core module.
+
+#ifndef OMAHA_CORE_CORE_METRICS_H_
+#define OMAHA_CORE_CORE_METRICS_H_
+
+#include "omaha/statsreport/metrics.h"
+
+namespace omaha {
+
+// Core process working set and peak working set.
+DECLARE_METRIC_integer(core_working_set);
+DECLARE_METRIC_integer(core_peak_working_set);
+
+// Core process handle count.
+DECLARE_METRIC_integer(core_handle_count);
+
+// Core process uptime, kernel, and user times.
+DECLARE_METRIC_integer(core_uptime_ms);
+DECLARE_METRIC_integer(core_kernel_time_ms);
+DECLARE_METRIC_integer(core_user_time_ms);
+
+// How much free space is availble on the current drive where Omaha is
+// installed.
+DECLARE_METRIC_integer(core_disk_space_available);
+
+// How many worker and code red processes are started by the core.
+DECLARE_METRIC_count(core_worker_total);
+DECLARE_METRIC_count(core_worker_succeeded);
+DECLARE_METRIC_count(core_cr_total);
+DECLARE_METRIC_count(core_cr_succeeded);
+
+// The period of code red checks.
+DECLARE_METRIC_integer(core_cr_expected_timer_interval_ms);
+DECLARE_METRIC_integer(core_cr_actual_timer_interval_ms);
+
+// How many times StartCrashHandler() was called.
+DECLARE_METRIC_count(core_start_crash_handler_total);
+// How many times StartCrashHandler() succeeded.
+DECLARE_METRIC_count(core_start_crash_handler_succeeded);
+
+// How many times core runs because the service and/or scheduled task is not
+// installed.
+DECLARE_METRIC_count(core_run_scheduled_task_missing);
+DECLARE_METRIC_count(core_run_service_missing);
+}  // namespace omaha
+
+#endif  // OMAHA_CORE_CORE_METRICS_H_
+
diff --git a/core/core_unittest.cc b/core/core_unittest.cc
new file mode 100644
index 0000000..34e9cb3
--- /dev/null
+++ b/core/core_unittest.cc
@@ -0,0 +1,105 @@
+// 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/const_object_names.h"
+#include "omaha/common/error.h"
+#include "omaha/common/scoped_any.h"
+#include "omaha/common/thread.h"
+#include "omaha/common/utils.h"
+#include "omaha/core/core.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+// Runs the core on a different thread. Since the core captures the thread id
+// in its constructor, the core instance must be created on this thread, not
+// on the main thread.
+class CoreRunner : public Runnable {
+ public:
+  explicit CoreRunner(bool is_machine) : is_machine_(is_machine) {}
+  virtual ~CoreRunner() {}
+
+ private:
+  virtual void Run() {
+    Core core;
+    core.Main(is_machine_, true);         // Run the crash handler.
+  }
+
+  bool is_machine_;
+  DISALLOW_EVIL_CONSTRUCTORS(CoreRunner);
+};
+
+}  // namespace
+
+class CoreTest : public testing::Test {
+ public:
+  CoreTest() : is_machine_(false) {}
+
+  virtual void SetUp() {
+    ASSERT_HRESULT_SUCCEEDED(IsSystemProcess(&is_machine_));
+
+    NamedObjectAttributes attr;
+    GetNamedObjectAttributes(kShutdownEvent, is_machine_, &attr);
+    reset(shutdown_event_, ::CreateEvent(&attr.sa, true, false, attr.name));
+    ASSERT_TRUE(shutdown_event_);
+  }
+
+  virtual void TearDown() {
+  }
+
+  HRESULT SignalShutdownEvent() {
+    EXPECT_TRUE(valid(shutdown_event_));
+    return ::SetEvent(get(shutdown_event_)) ? S_OK : HRESULTFromLastError();
+  }
+
+  HRESULT ResetShutdownEvent() {
+    EXPECT_TRUE(valid(shutdown_event_));
+    return ::ResetEvent(get(shutdown_event_)) ? S_OK : HRESULTFromLastError();
+  }
+
+ protected:
+  bool is_machine_;
+  scoped_event shutdown_event_;
+};
+
+// Tests the core shutdown mechanism.
+TEST_F(CoreTest, Shutdown) {
+  // Signal existing core instances to shutdown, otherwise new instances
+  // can't start.
+  ASSERT_HRESULT_SUCCEEDED(SignalShutdownEvent());
+  ::Sleep(0);
+  ASSERT_HRESULT_SUCCEEDED(ResetShutdownEvent());
+
+  // Start a thread to run the core, signal the core to exit, and wait a while
+  // for the thread to exit. Terminate the thread if it is still running.
+  Thread thread;
+  CoreRunner core_runner(is_machine_);
+  EXPECT_TRUE(thread.Start(&core_runner));
+
+  // Give the core a little time to run before signaling it to exit.
+  ::Sleep(100);
+  EXPECT_HRESULT_SUCCEEDED(SignalShutdownEvent());
+  EXPECT_TRUE(thread.WaitTillExit(2000));
+  if (thread.Running()) {
+    thread.Terminate(-1);
+  }
+  EXPECT_HRESULT_SUCCEEDED(ResetShutdownEvent());
+}
+
+}  // namespace omaha
+
diff --git a/core/crash_handler.cc b/core/crash_handler.cc
new file mode 100644
index 0000000..cf87b2c
--- /dev/null
+++ b/core/crash_handler.cc
@@ -0,0 +1,132 @@
+// 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 CrashHandler is a long-lived Omaha process. It runs one instance for the
+// machine and one instance for each user session, including console and TS
+// sessions. If the user has turned off crash reporting, this process will not
+// run.
+
+#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 {
+
+CrashHandler::CrashHandler()
+    : is_system_(false),
+      main_thread_id_(0) {
+  CORE_LOG(L1, (_T("[CrashHandler::CrashHandler]")));
+}
+
+CrashHandler::~CrashHandler() {
+  CORE_LOG(L1, (_T("[CrashHandler::~CrashHandler]")));
+  Crash::StopServer();
+}
+
+HRESULT CrashHandler::Main(bool is_system) {
+  if (!ConfigManager::Instance()->CanCollectStats(is_system)) {
+    return S_OK;
+  }
+
+  main_thread_id_ = ::GetCurrentThreadId();
+  is_system_ = is_system;
+
+  NamedObjectAttributes single_CrashHandler_attr;
+  GetNamedObjectAttributes(kCrashHandlerSingleInstance,
+                           is_system,
+                           &single_CrashHandler_attr);
+  ProgramInstance instance(single_CrashHandler_attr.name);
+  bool is_already_running = !instance.EnsureSingleInstance();
+  if (is_already_running) {
+    OPT_LOG(L1, (_T("[another CrashHandler instance is already running]")));
+    return S_OK;
+  }
+
+  // Start the crash handler.
+  HRESULT hr = Crash::StartServer();
+  if (FAILED(hr)) {
+    OPT_LOG(LW, (_T("[Failed to start crash handler][0x%08x]"), hr));
+  }
+
+  // Force the main thread to create a message queue so any future WM_QUIT
+  // message posted by the ShutdownHandler will be received. If the main
+  // thread does not have a message queue, the message can be lost.
+  MSG msg = {0};
+  ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+  reactor_.reset(new Reactor);
+  shutdown_handler_.reset(new ShutdownHandler);
+  hr = shutdown_handler_->Initialize(reactor_.get(), this, is_system_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Start processing messages and events from the system.
+  return DoRun();
+}
+
+// Signals the CrashHandler to shutdown. The shutdown method is called by a
+// thread running in the thread pool. It posts a WM_QUIT to the main thread,
+// which causes it to break out of the message loop. If the message can't be
+// posted, it terminates the process unconditionally.
+HRESULT CrashHandler::Shutdown() {
+  OPT_LOG(L1, (_T("[CrashHandler::Shutdown]")));
+  ASSERT1(::GetCurrentThreadId() != main_thread_id_);
+  if (::PostThreadMessage(main_thread_id_, WM_QUIT, 0, 0)) {
+    return S_OK;
+  }
+
+  ASSERT(false, (_T("Failed to post WM_QUIT")));
+  uint32 exit_code = static_cast<uint32>(E_ABORT);
+  VERIFY1(::TerminateProcess(::GetCurrentProcess(), exit_code));
+  return S_OK;
+}
+
+HRESULT CrashHandler::DoRun() {
+  OPT_LOG(L1, (_T("[CrashHandler::DoRun]")));
+
+  // Trim the process working set to minimum. It does not need a more complex
+  // algorithm for now. Likely the working set will increase slightly over time
+  // as the CrashHandler is handling events.
+  VERIFY1(::SetProcessWorkingSetSize(::GetCurrentProcess(),
+                                     static_cast<uint32>(-1),
+                                     static_cast<uint32>(-1)));
+  return DoHandleEvents();
+}
+
+HRESULT CrashHandler::DoHandleEvents() {
+  CORE_LOG(L1, (_T("[CrashHandler::DoHandleEvents]")));
+  MSG msg = {0};
+  int result = 0;
+  while ((result = ::GetMessage(&msg, 0, 0, 0)) != 0) {
+    ::DispatchMessage(&msg);
+    if (result == -1) {
+      break;
+    }
+  }
+  CORE_LOG(L3, (_T("[GetMessage returned %d]"), result));
+  return (result != -1) ? S_OK : HRESULTFromLastError();
+}
+
+}  // namespace omaha
+
diff --git a/core/crash_handler.h b/core/crash_handler.h
new file mode 100644
index 0000000..cff0563
--- /dev/null
+++ b/core/crash_handler.h
@@ -0,0 +1,66 @@
+// 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_CORE_CRASH_HANDLER_H_
+#define OMAHA_CORE_CRASH_HANDLER_H_
+
+#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 "third_party/breakpad/src/client/windows/crash_generation/client_info.h"
+#include "third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.h"
+
+namespace omaha {
+
+class Reactor;
+class ShutdownHandler;
+
+class CrashHandler : public ShutdownCallback {
+ public:
+  CrashHandler();
+  virtual ~CrashHandler();
+
+  // Executes the instance entry point with given parameters.
+  HRESULT Main(bool is_system);
+
+  Reactor* reactor() const { return reactor_.get(); }
+  bool is_system() const { return is_system_; }
+
+ private:
+  typedef google_breakpad::CrashGenerationServer CrashGenerationServer;
+
+  // ShutdownCallback interface.
+  // Signals the CrashHandler to stop handling events and exit.
+  virtual HRESULT Shutdown();
+
+  HRESULT DoRun();
+  HRESULT DoHandleEvents();
+
+  bool is_system_;
+
+  DWORD main_thread_id_;  // The id of the thread that runs CrashHandler::Main.
+
+  scoped_ptr<Reactor>               reactor_;
+  scoped_ptr<ShutdownHandler>       shutdown_handler_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CrashHandler);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_CORE_CRASH_HANDLER_H_
+
diff --git a/core/google_update_core.cc b/core/google_update_core.cc
new file mode 100644
index 0000000..58ad6f2
--- /dev/null
+++ b/core/google_update_core.cc
@@ -0,0 +1,153 @@
+// 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/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"
+
+namespace omaha {
+
+GoogleUpdateCore::GoogleUpdateCore() {
+  CORE_LOG(L3, (_T("[GoogleUpdateCore::GoogleUpdateCore]")));
+}
+
+GoogleUpdateCore::~GoogleUpdateCore() {
+  CORE_LOG(L3, (_T("[GoogleUpdateCore::~GoogleUpdateCore]")));
+}
+
+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]")
+                _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);
+
+  if (!(IsGuid(app_guid) && cmd_id && _tcslen(cmd_id) && proc_handle)) {
+    return E_INVALIDARG;
+  }
+
+  scoped_process caller_proc_handle;
+  HRESULT hr = OpenCallerProcessHandle(caller_proc_id,
+                                       address(caller_proc_handle));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to open caller's handle][0x%x]"), hr));
+    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);
+}
+
+HRESULT GoogleUpdateCore::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();
+}
+
+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);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to launch cmd][%s][0x%08x]"), *cmd, 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.
+      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.
+  if (!res) {
+    hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[failed to duplicate the handle][0x%08x]"), hr));
+    return hr;
+  }
+
+  // Transfer the ownership of the new handle to the caller. The caller must
+  // close this handle.
+  *proc_handle = reinterpret_cast<ULONG_PTR>(release(duplicate_proc_handle));
+
+  return S_OK;
+}
+
+
+}  // namespace omaha
+
diff --git a/core/google_update_core.h b/core/google_update_core.h
new file mode 100644
index 0000000..d6f19ed
--- /dev/null
+++ b/core/google_update_core.h
@@ -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.
+// ========================================================================
+
+#ifndef OMAHA_CORE_GOOGLE_UPDATE_CORE_H_
+#define OMAHA_CORE_GOOGLE_UPDATE_CORE_H_
+
+#include <windows.h>
+#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"
+
+// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
+#include "goopdate/google_update_idl.h"
+
+namespace omaha {
+
+// TODO(omaha): might need to synchronize on the GoogleUpdateCoreProxy.
+typedef SharedMemoryProxy<IGoogleUpdateCore, FakeGLock> GoogleUpdateCoreProxy;
+
+class ATL_NO_VTABLE GoogleUpdateCore
+    : public CComObjectRootEx<CComMultiThreadModel>,
+      public CComCoClass<GoogleUpdateCore>,
+      public IGoogleUpdateCore {
+ public:
+  GoogleUpdateCore();
+  virtual ~GoogleUpdateCore();
+
+  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)
+    COM_INTERFACE_ENTRY(IGoogleUpdateCore)
+  END_COM_MAP()
+
+  // Launches a command line elevated.
+  STDMETHOD(LaunchCmdElevated)(const WCHAR* app_guid,
+                               const WCHAR* cmd_id,
+                               DWORD caller_proc_id,
+                               ULONG_PTR* proc_handle);
+ private:
+  // If the COM caller has permissions for process proc_id, opens and returns
+  // 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(GoogleUpdateCore);
+};
+
+}  // 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
new file mode 100644
index 0000000..d0db23b
--- /dev/null
+++ b/core/google_update_core_unittest.cc
@@ -0,0 +1,434 @@
+// 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/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/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 {
+
+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;
+    }
+    System::AdjustPrivilege(SE_DEBUG_NAME, true);
+    TerminateAllGoogleUpdateProcesses();
+    SetupRegistry();
+  }
+
+  virtual void TearDown() {
+    if (!vista_util::IsUserAdmin()) {
+      return;
+    }
+    TerminateAllGoogleUpdateProcesses();
+    TeardownRegistry();
+  }
+
+  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) {
+      break;
+    }
+
+    ::Sleep(1000);
+  }
+
+  return handle;
+}
+
+void GoogleUpdateCoreTest::DoLaunchCmdElevatedTests(IUnknown* core_object) {
+  CComQIPtr<IGoogleUpdateCore> google_update_core = core_object;
+  EXPECT_TRUE(google_update_core != NULL);
+  if (!google_update_core) {
+    return;
+  }
+
+  ULONG_PTR proc_handle = 0;
+  DWORD caller_proc_id = ::GetCurrentProcessId();
+
+  // Returns ERROR_BAD_IMPERSONATION_LEVEL when explicit security blanket is not
+  // set.
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_BAD_IMPERSONATION_LEVEL),
+            google_update_core->LaunchCmdElevated(kGoogleUpdateAppId,
+                                                  _T("cmd"),
+                                                  caller_proc_id,
+                                                  &proc_handle));
+  EXPECT_EQ(0, proc_handle);
+
+  // Sets a security blanket that will allow the server to impersonate the
+  // client.
+  EXPECT_SUCCEEDED(::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));
+
+  // Returns GOOPDATE_E_CORE_MISSING_CMD when the command is missing in
+  // the registry.
+  EXPECT_EQ(GOOPDATE_E_CORE_MISSING_CMD,
+            google_update_core->LaunchCmdElevated(kGoogleUpdateAppId,
+                                                  _T("cmd"),
+                                                  caller_proc_id,
+                                                  &proc_handle));
+  EXPECT_EQ(0, proc_handle);
+
+  // Returns E_INVALIDARG when the app_guid is not a guid.
+  EXPECT_EQ(E_INVALIDARG,
+            google_update_core->LaunchCmdElevated(_T("noguid"),
+                                                  _T("cmd"),
+                                                  caller_proc_id,
+                                                  &proc_handle));
+
+  EXPECT_SUCCEEDED(google_update_core->LaunchCmdElevated(kGoogleUpdateAppId,
+                                                         _T("fc"),
+                                                         caller_proc_id,
+                                                         &proc_handle));
+  EXPECT_NE(0, proc_handle);
+
+  // TODO(Omaha): Perhaps attempt some negative tests here, either by testing
+  // the permissions on the handle explicitly, or by attempting VM operations or
+  // such on the process handle, since it's a serious security issue if the
+  // handle permissions are too wide.
+  HANDLE handle = reinterpret_cast<HANDLE>(proc_handle);
+  EXPECT_NE(WAIT_FAILED, ::WaitForSingleObject(handle, 10000));
+  EXPECT_TRUE(::CloseHandle(handle));
+}
+
+TEST_F(GoogleUpdateCoreTest, LaunchCmdElevated_CoreNotRunning) {
+  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));
+}
+
+TEST_F(GoogleUpdateCoreTest, LaunchCmdElevated) {
+  if (!vista_util::IsUserAdmin()) {
+    SUCCEED() << "\tTest did not run because the user is not an admin.";
+    return;
+  }
+
+  // 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) {
+  CComPtr<IUnknown> service_com;
+  EXPECT_SUCCEEDED(
+      service_com.CoCreateInstance(__uuidof(GoogleUpdateCoreClass)));
+
+  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();
+}
+
+}  // namespace omaha
+
diff --git a/core/legacy_manifest_handler.cc b/core/legacy_manifest_handler.cc
new file mode 100644
index 0000000..fd8371f
--- /dev/null
+++ b/core/legacy_manifest_handler.cc
@@ -0,0 +1,92 @@
+// 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
new file mode 100644
index 0000000..a387dd3
--- /dev/null
+++ b/core/legacy_manifest_handler.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.
+// ========================================================================
+//
+// 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
new file mode 100644
index 0000000..8394072
--- /dev/null
+++ b/core/scheduler.cc
@@ -0,0 +1,129 @@
+// 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/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/core/core.h"
+#include "omaha/core/core_metrics.h"
+#include "omaha/goopdate/config_manager.h"
+
+namespace omaha {
+
+Scheduler::Scheduler(const Core& core)
+    : core_(core) {
+  CORE_LOG(L1, (_T("[Scheduler::Scheduler]")));
+}
+
+Scheduler::~Scheduler() {
+  CORE_LOG(L1, (_T("[Scheduler::~Scheduler]")));
+
+  if (update_timer_.get()) {
+    update_timer_.reset(NULL);
+  }
+
+  if (code_red_timer_.get()) {
+    code_red_timer_.reset(NULL);
+  }
+
+  if (timer_queue_) {
+    // The destructor blocks on deleting the timer queue and it waits for
+    // all timer callbacks to complete.
+    ::DeleteTimerQueueEx(timer_queue_, INVALID_HANDLE_VALUE);
+  }
+}
+
+HRESULT Scheduler::Initialize() {
+  CORE_LOG(L1, (_T("[Scheduler::Initialize]")));
+
+  timer_queue_ = ::CreateTimerQueue();
+  if (!timer_queue_) {
+    return HRESULTFromLastError();
+  }
+
+  cr_debug_timer_.reset(new HighresTimer);
+
+  update_timer_.reset(new QueueTimer(timer_queue_,
+                                     &Scheduler::TimerCallback,
+                                     this));
+  code_red_timer_.reset(new QueueTimer(timer_queue_,
+                                       &Scheduler::TimerCallback,
+                                       this));
+
+  ConfigManager* config_manager = ConfigManager::Instance();
+  int cr_timer_interval_ms = config_manager->GetCodeRedTimerIntervalMs();
+  VERIFY1(SUCCEEDED(ScheduleCodeRedTimer(cr_timer_interval_ms)));
+
+  int au_timer_interval_ms = config_manager->GetUpdateWorkerStartUpDelayMs();
+  VERIFY1(SUCCEEDED(ScheduleUpdateTimer(au_timer_interval_ms)));
+
+  return S_OK;
+}
+
+void Scheduler::TimerCallback(QueueTimer* timer) {
+  ASSERT1(timer);
+  Scheduler* scheduler = static_cast<Scheduler*>(timer->ctx());
+  ASSERT1(scheduler);
+  scheduler->HandleCallback(timer);
+}
+
+// First, do the useful work and then reschedule the timer. Otherwise, it is
+// possible that timer notifications overlap, and the timer can't be further
+// rescheduled: http://b/1228095
+void Scheduler::HandleCallback(QueueTimer* timer) {
+  ConfigManager* config_manager = ConfigManager::Instance();
+  if (update_timer_.get() == timer) {
+    core_.StartUpdateWorker();
+    int au_timer_interval_ms = config_manager->GetAutoUpdateTimerIntervalMs();
+    VERIFY1(SUCCEEDED(ScheduleUpdateTimer(au_timer_interval_ms)));
+  } else if (code_red_timer_.get() == timer) {
+    core_.StartCodeRed();
+    int actual_time_ms = static_cast<int>(cr_debug_timer_->GetElapsedMs());
+    metric_core_cr_actual_timer_interval_ms = actual_time_ms;
+    CORE_LOG(L3, (_T("[code red actual period][%d ms]"), actual_time_ms));
+    int cr_timer_interval_ms = config_manager->GetCodeRedTimerIntervalMs();
+    VERIFY1(SUCCEEDED(ScheduleCodeRedTimer(cr_timer_interval_ms)));
+  } else {
+    ASSERT1(false);
+  }
+
+  // Since core is a long lived process, aggregate its metrics once in a while.
+  core_.AggregateMetrics();
+}
+
+HRESULT Scheduler::ScheduleUpdateTimer(int interval_ms) {
+  HRESULT hr = update_timer_->Start(interval_ms, 0, WT_EXECUTEONLYONCE);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[can't start update queue timer][0x%08x]"), hr));
+  }
+  return hr;
+}
+
+HRESULT Scheduler::ScheduleCodeRedTimer(int interval_ms) {
+  metric_core_cr_expected_timer_interval_ms = interval_ms;
+  cr_debug_timer_->Start();
+  HRESULT hr = code_red_timer_->Start(interval_ms, 0, WT_EXECUTEONLYONCE);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[can't start Code Red queue timer][0x%08x]"), hr));
+  }
+  return hr;
+}
+
+}  // namespace omaha
+
diff --git a/core/scheduler.h b/core/scheduler.h
new file mode 100644
index 0000000..88ea5c8
--- /dev/null
+++ b/core/scheduler.h
@@ -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.
+// ========================================================================
+
+// TODO(omaha): consider using a waitable timer registered with the reactor. It
+// seems a lot less code than using the QueueTimer class.
+
+#ifndef OMAHA_CORE_SCHEDULER_H__
+#define OMAHA_CORE_SCHEDULER_H__
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+namespace omaha {
+
+class Core;
+class HighresTimer;
+class QueueTimer;
+
+class Scheduler {
+ public:
+  explicit Scheduler(const Core& core);
+  ~Scheduler();
+
+  // Starts the scheduler.
+  HRESULT Initialize();
+
+ private:
+  static void TimerCallback(QueueTimer* timer);
+  void HandleCallback(QueueTimer* timer);
+  HRESULT ScheduleUpdateTimer(int interval_ms);
+  HRESULT ScheduleCodeRedTimer(int interval_ms);
+
+  const Core& core_;
+  HANDLE timer_queue_;
+  scoped_ptr<QueueTimer> update_timer_;
+  scoped_ptr<QueueTimer> code_red_timer_;
+
+  // Measures the actual time interval between code red events for debugging
+  // purposes. The timer is started when a code red alarm is set and then,
+  // the value of the timer is read when the alarm goes off.
+  scoped_ptr<HighresTimer> cr_debug_timer_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Scheduler);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_CORE_SCHEDULER_H__
+
diff --git a/core/system_monitor.cc b/core/system_monitor.cc
new file mode 100644
index 0000000..1520268
--- /dev/null
+++ b/core/system_monitor.cc
@@ -0,0 +1,144 @@
+// 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/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"
+
+namespace omaha {
+
+SystemMonitor::SystemMonitor(bool is_machine)
+    : is_machine_(is_machine) {
+  ::InterlockedExchangePointer(reinterpret_cast<void**>(&observer_), NULL);
+}
+
+SystemMonitor::~SystemMonitor() {
+  if (m_hWnd) {
+    VERIFY1(DestroyWindow());
+  }
+}
+
+HRESULT SystemMonitor::Initialize(bool monitor_registry) {
+  if (monitor_registry) {
+    registry_monitor_.reset(new RegistryMonitor);
+    if (SUCCEEDED(registry_monitor_->Initialize())) {
+      HKEY root_key = is_machine_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+      VERIFY1(SUCCEEDED(registry_monitor_->MonitorValue(
+          root_key,
+          GOOPDATE_MAIN_KEY,
+          kRegValueLastChecked,
+          REG_DWORD,
+          RegistryValueChangeCallback,
+          this)));
+      VERIFY1(SUCCEEDED(registry_monitor_->MonitorKey(
+          root_key,
+          GOOPDATE_REG_RELATIVE_CLIENTS,
+          RegistryKeyChangeCallback,
+          this)));
+      VERIFY1(SUCCEEDED(registry_monitor_->StartMonitoring()));
+    }
+  }
+
+  // Create a window to receive broadcast messages.
+  const TCHAR kWindowTitle[] = _T("{2D905E07-FC38-4b89-83E1-931D3630937F}");
+  VERIFY1(Create(NULL, NULL, kWindowTitle));
+  return S_OK;
+}
+
+LRESULT SystemMonitor::OnPowerBroadcast(UINT, WPARAM wparam,
+                                        LPARAM, BOOL& handled) {
+  CORE_LOG(L3, (_T("[SystemMonitor::OnPowerBroadcast][wparam %d]"), wparam));
+  UNREFERENCED_PARAMETER(wparam);
+  handled = true;
+  return 0;
+}
+
+LRESULT SystemMonitor::OnEndSession(UINT, WPARAM wparam,
+                                    LPARAM lparam, BOOL& handled) {
+  CORE_LOG(L3, (_T("[SystemMonitor::OnEndSession][wparam %d][lparam %x]"),
+                wparam, lparam));
+  UNREFERENCED_PARAMETER(wparam);
+  UNREFERENCED_PARAMETER(lparam);
+  handled = true;
+  return 0;
+}
+
+LRESULT SystemMonitor::OnQueryEndSession(UINT, WPARAM,
+                                         LPARAM lparam, BOOL& handled) {
+  CORE_LOG(L3, (_T("[SystemMonitor::OnQueryEndSession][lparam %x]"), lparam));
+  UNREFERENCED_PARAMETER(lparam);
+  handled = true;
+  return TRUE;
+}
+
+LRESULT SystemMonitor::OnWTSSessionChange(UINT, WPARAM wparam,
+                                          LPARAM lparam, BOOL& handled) {
+  CORE_LOG(L3, (_T("[SystemMonitor::OnWTSSessionChange][wparam %x][lparam %d]"),
+                wparam, lparam));
+  UNREFERENCED_PARAMETER(wparam);
+  UNREFERENCED_PARAMETER(lparam);
+  handled = true;
+  return 0;
+}
+
+void SystemMonitor::RegistryValueChangeCallback(const TCHAR* key_name,
+                                                const TCHAR* value_name,
+                                                RegistryChangeType change_type,
+                                                const void* new_value_data,
+                                                void* user_data) {
+  ASSERT1(key_name);
+  ASSERT1(value_name);
+  ASSERT1(user_data);
+
+  ASSERT1(_tcscmp(value_name, kRegValueLastChecked) == 0);
+
+  UNREFERENCED_PARAMETER(key_name);
+  UNREFERENCED_PARAMETER(value_name);
+  UNREFERENCED_PARAMETER(new_value_data);
+
+  SystemMonitor* system_monitor = static_cast<SystemMonitor*>(user_data);
+  if (change_type == REGISTRY_CHANGE_TYPE_DELETE &&
+      system_monitor->observer_) {
+    system_monitor->observer_->LastCheckedDeleted();
+  }
+}
+
+void SystemMonitor::RegistryKeyChangeCallback(const TCHAR* key_name,
+                                              void* user_data) {
+  ASSERT1(key_name);
+  ASSERT1(user_data);
+
+  UNREFERENCED_PARAMETER(key_name);
+  ASSERT1(_tcscmp(key_name, GOOPDATE_REG_RELATIVE_CLIENTS) == 0);
+
+  SystemMonitor* system_monitor = static_cast<SystemMonitor*>(user_data);
+  if (!system_monitor->observer_) {
+    return;
+  }
+
+  const bool is_machine = system_monitor->is_machine_;
+  size_t num_clients(0);
+  if (SUCCEEDED(goopdate_utils::GetNumClients(is_machine, &num_clients)) &&
+      num_clients <= 1) {
+    system_monitor->observer_->NoRegisteredClients();
+  }
+}
+
+}  // namespace omaha
diff --git a/core/system_monitor.h b/core/system_monitor.h
new file mode 100644
index 0000000..d27a762
--- /dev/null
+++ b/core/system_monitor.h
@@ -0,0 +1,98 @@
+// 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.
+// ========================================================================
+
+// SystemMonitor receives session messages and power management messages.
+
+#ifndef OMAHA_CORE_SYSTEM_MONITOR_H_
+#define OMAHA_CORE_SYSTEM_MONITOR_H_
+
+#include <atlbase.h>
+#include <atlwin.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/common/registry_monitor_manager.h"
+
+namespace omaha {
+
+class SystemMonitorObserver {
+ public:
+  virtual ~SystemMonitorObserver() {}
+
+  // Called when 'LastChecked' registry value is deleted.
+  virtual void LastCheckedDeleted() = 0;
+
+  // Called when there are no other clients that are registered besides Omaha.
+  virtual void NoRegisteredClients() = 0;
+};
+
+class SystemMonitor
+    : public CWindowImpl<SystemMonitor,
+                         CWindow,
+                         CWinTraits<WS_OVERLAPPED, WS_EX_TOOLWINDOW> > {
+ public:
+  explicit SystemMonitor(bool is_machine);
+  ~SystemMonitor();
+
+  HRESULT Initialize(bool monitor_registry);
+
+  void set_observer(SystemMonitorObserver* observer) {
+    ::InterlockedExchangePointer(reinterpret_cast<void**>(&observer_),
+                                 observer);
+  }
+
+  BEGIN_MSG_MAP(SystemMonitor)
+    MESSAGE_HANDLER(WM_POWERBROADCAST,    OnPowerBroadcast)
+    MESSAGE_HANDLER(WM_QUERYENDSESSION,   OnQueryEndSession)
+    MESSAGE_HANDLER(WM_ENDSESSION,        OnEndSession)
+    MESSAGE_HANDLER(WM_WTSSESSION_CHANGE, OnWTSSessionChange)
+  END_MSG_MAP()
+
+ private:
+  // Notifies the system monitor that a power-management event has occurred.
+  LRESULT OnPowerBroadcast(UINT msg, WPARAM wparam,
+                           LPARAM lparam, BOOL& handled);
+
+  // Notifies the system monitor a shutdown has been requested.
+  LRESULT OnQueryEndSession(UINT msg, WPARAM wparam,
+                            LPARAM lparam, BOOL& handled);
+
+  // Notifies the system monitor whether the session is ending.
+  LRESULT OnEndSession(UINT msg, WPARAM wparam,
+                       LPARAM lparam, BOOL& handled);
+
+  // Notifies the system monitor about changes in the session state.
+  LRESULT OnWTSSessionChange(UINT msg, WPARAM wparam,
+                             LPARAM lparam, BOOL& handled);
+
+  static void RegistryValueChangeCallback(const TCHAR* key_name,
+                                          const TCHAR* value_name,
+                                          RegistryChangeType change_type,
+                                          const void* new_value_data,
+                                          void* user_data);
+
+  static void RegistryKeyChangeCallback(const TCHAR* key_name,
+                                        void* user_data);
+
+  scoped_ptr<RegistryMonitor> registry_monitor_;
+  bool is_machine_;
+  SystemMonitorObserver* observer_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(SystemMonitor);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_CORE_SYSTEM_MONITOR_H_
+
diff --git a/core/system_monitor_unittest.cc b/core/system_monitor_unittest.cc
new file mode 100644
index 0000000..c68727a
--- /dev/null
+++ b/core/system_monitor_unittest.cc
@@ -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.
+// ========================================================================
+
+#include "omaha/common/constants.h"
+#include "omaha/common/path.h"
+#include "omaha/common/reg_key.h"
+#include "omaha/common/synchronized.h"
+#include "omaha/core/system_monitor.h"
+#include "omaha/goopdate/const_goopdate.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class SystemMonitorTest
+    : public testing::Test,
+      public SystemMonitorObserver {
+ protected:
+  virtual void SetUp() {
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+    OverrideRegistryHives(kRegistryHiveOverrideRoot);
+    gate_.reset(new Gate);
+  }
+
+  virtual void TearDown() {
+    gate_.reset();
+    RestoreRegistryHives();
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+  }
+
+  // SystemMonitorObserver interface.
+  virtual void LastCheckedDeleted() {
+    gate_->Open();
+  }
+
+  virtual void NoRegisteredClients() {
+    gate_->Open();
+  }
+
+  void MonitorLastCheckedTest(bool is_machine);
+  void MonitorClientsTest(bool is_machine);
+
+  scoped_ptr<Gate> gate_;
+};
+
+void SystemMonitorTest::MonitorLastCheckedTest(bool is_machine) {
+  const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
+  DWORD last_checked_value(1);
+  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(key_name,
+                                            kRegValueLastChecked,
+                                            last_checked_value));
+  SystemMonitor system_monitor(is_machine);
+  system_monitor.set_observer(this);
+  ASSERT_HRESULT_SUCCEEDED(system_monitor.Initialize(true));
+
+  // Trigger the callback first time.
+  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteValue(key_name,
+                                               kRegValueLastChecked));
+  EXPECT_TRUE(gate_->Wait(1000));
+  EXPECT_FALSE(RegKey::HasValue(key_name, kRegValueLastChecked));
+
+  // Trigger the callback second time.
+  last_checked_value = 2;
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(key_name,
+                                            kRegValueLastChecked,
+                                            last_checked_value));
+
+  // It takes a while for the registry monitor to detect the changes. There are
+  // two changes here: setting and deleting the values.
+  ::Sleep(50);
+  EXPECT_TRUE(gate_->Close());
+  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteValue(key_name,
+                                               kRegValueLastChecked));
+  EXPECT_TRUE(gate_->Wait(1000));
+  EXPECT_FALSE(RegKey::HasValue(key_name, kRegValueLastChecked));
+}
+
+void SystemMonitorTest::MonitorClientsTest(bool is_machine) {
+  const TCHAR* key_name = is_machine ? MACHINE_REG_CLIENTS : USER_REG_CLIENTS;
+  const TCHAR guid[] = _T("{4AAF2315-B7C8-4633-A1BA-884EFAB755F7}");
+
+  CString app_guid = ConcatenatePath(key_name, guid);
+  CString omaha_guid = ConcatenatePath(key_name, kGoogleUpdateAppId);
+  const TCHAR* keys_to_create[] = { app_guid, omaha_guid };
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKeys(keys_to_create,
+                                              arraysize(keys_to_create)));
+
+  SystemMonitor system_monitor(is_machine);
+  system_monitor.set_observer(this);
+  EXPECT_HRESULT_SUCCEEDED(system_monitor.Initialize(true));
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteKey(keys_to_create[0], true));
+  EXPECT_TRUE(gate_->Wait(1000));
+}
+
+TEST_F(SystemMonitorTest, SystemMonitor) {
+  SystemMonitor system_monitor(false);
+  ASSERT_HRESULT_SUCCEEDED(system_monitor.Initialize(false));
+  EXPECT_EQ(0, system_monitor.SendMessage(WM_POWERBROADCAST, 0, 0));
+  EXPECT_EQ(TRUE, system_monitor.SendMessage(WM_QUERYENDSESSION, 0, 0));
+  EXPECT_EQ(0, system_monitor.SendMessage(WM_ENDSESSION, 0, 0));
+  EXPECT_EQ(0, system_monitor.SendMessage(WM_WTSSESSION_CHANGE, 0, 0));
+}
+
+// Tests the callback gets called when the "LastChecked" is deleted and
+// the value is not recreated automatically by the monitor.
+TEST_F(SystemMonitorTest, DeleteLastChecked_User) {
+  MonitorLastCheckedTest(false);
+}
+
+TEST_F(SystemMonitorTest, DeleteLastChecked_Machine) {
+  MonitorLastCheckedTest(true);
+}
+
+TEST_F(SystemMonitorTest, MonitorClients_User) {
+  MonitorClientsTest(false);
+}
+
+TEST_F(SystemMonitorTest, MonitorClients_Machine) {
+  MonitorClientsTest(true);
+}
+
+}  // namespace omaha
+
diff --git a/data/GoogleUpdate.ini b/data/GoogleUpdate.ini
new file mode 100644
index 0000000..404a1b9
--- /dev/null
+++ b/data/GoogleUpdate.ini
@@ -0,0 +1,6 @@
+[LoggingLevel]
+LC_OPT=1
+
+[LoggingSettings]
+EnableLogging=1
+; MaxLogFileSize=1000000
diff --git a/data/OmahaTestCert.cer b/data/OmahaTestCert.cer
new file mode 100644
index 0000000..74d851b
--- /dev/null
+++ b/data/OmahaTestCert.cer
Binary files differ
diff --git a/data/OmahaTestCert.pfx b/data/OmahaTestCert.pfx
new file mode 100644
index 0000000..f0ddb4b
--- /dev/null
+++ b/data/OmahaTestCert.pfx
Binary files differ
diff --git a/data/certificate-with-private-key.pfx b/data/certificate-with-private-key.pfx
new file mode 100644
index 0000000..7eaa2da
--- /dev/null
+++ b/data/certificate-with-private-key.pfx
Binary files differ
diff --git a/data/certificate-without-private-key.cer b/data/certificate-without-private-key.cer
new file mode 100644
index 0000000..b456dfc
--- /dev/null
+++ b/data/certificate-without-private-key.cer
Binary files differ
diff --git a/data/declaration.txt b/data/declaration.txt
new file mode 100644
index 0000000..5a5ff02
--- /dev/null
+++ b/data/declaration.txt
@@ -0,0 +1,109 @@
+The Declaration of Independence of the Thirteen Colonies
+In CONGRESS, July 4, 1776 
+
+The unanimous Declaration of the thirteen united States of America, 
+
+When in the Course of human events, it becomes necessary for one people to dissolve the political bands which have connected them with another, and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which impel them to the separation. 
+
+We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness. --That to secure these rights, Governments are instituted among Men, deriving their just powers from the consent of the governed, --That whenever any Form of Government becomes destructive of these ends, it is the Right of the People to alter or to abolish it, and to institute new Government, laying its foundation on such principles and organizing its powers in such form, as to them shall seem most likely to effect their Safety and Happiness. Prudence, indeed, will dictate that Governments long established should not be changed for light and transient causes; and accordingly all experience hath shewn, that mankind are more disposed to suffer, while evils are sufferable, than to right themselves by abolishing the forms to which they are accustomed. But when a long train of abuses and usurpations, pursuing invariably the same Object evinces a design to reduce them under absolute Despotism, it is their right, it is their duty, to throw off such Government, and to provide new Guards for their future security. —Such has been the patient sufferance of these Colonies; and such is now the necessity which constrains them to alter their former Systems of Government. The history of the present King of Great Britain [George III] is a history of repeated injuries and usurpations, all having in direct object the establishment of an absolute Tyranny over these States. To prove this, let Facts be submitted to a candid world. 
+
+He has refused his Assent to Laws, the most wholesome and necessary for the public good. 
+
+He has forbidden his Governors to pass Laws of immediate and pressing importance, unless suspended in their operation till his Assent should be obtained; and when so suspended, he has utterly neglected to attend to them. 
+
+He has refused to pass other Laws for the accommodation of large districts of people, unless those people would relinquish the right of Representation in the Legislature, a right inestimable to them and formidable to tyrants only. 
+
+He has called together legislative bodies at places unusual, uncomfortable, and distant from the depository of their public Records, for the sole purpose of fatiguing them into compliance with his measures. 
+
+He has dissolved Representative Houses repeatedly, for opposing with manly firmness his invasions on the rights of the people. 
+
+He has refused for a long time, after such dissolutions, to cause others to be elected; whereby the Legislative powers, incapable of Annihilation, have returned to the People at large for their exercise; the State remaining in the mean time exposed to all the dangers of invasion from without, and convulsions within. 
+
+He has endeavoured to prevent the population of these States; for that purpose obstructing the Laws for Naturalization of Foreigners; refusing to pass others to encourage their migrations hither, and raising the conditions of new Appropriations of Lands. 
+
+He has obstructed the Administration of Justice, by refusing his Assent to Laws for establishing Judiciary powers. 
+
+He has made Judges dependent on his Will alone, for the tenure of their offices, and the amount and payment of their salaries. 
+
+He has erected a multitude of New Offices, and sent hither swarms of Officers to harass our people, and eat out their substance. 
+
+He has kept among us, in times of peace, Standing Armies without the consent of our legislatures. 
+
+He has affected to render the Military independent of and superior to the Civil power. 
+
+He has combined with others to subject us to a jurisdiction foreign to our constitution and unacknowledged by our laws; giving his Assent to their Acts of pretended Legislation: 
+
+For Quartering large bodies of armed troops among us: 
+
+For protecting them, by a mock Trial, from punishment for any Murders which they should commit on the Inhabitants of these States: 
+
+For cutting off our Trade with all parts of the world: 
+
+For imposing Taxes on us without our Consent: 
+
+For depriving us, in many cases, of the benefits of Trial by Jury: 
+
+For transporting us beyond Seas to be tried for pretended offences: 
+
+For abolishing the free System of English Laws in a neighbouring Province, establishing therein an Arbitrary government, and enlarging its Boundaries so as to render it at once an example and fit instrument for introducing the same absolute rule into these Colonies: 
+
+For taking away our Charters, abolishing our most valuable Laws, and altering fundamentally the Forms of our Governments: 
+
+For suspending our own Legislatures, and declaring themselves invested with power to legislate for us in all cases whatsoever. 
+
+He has abdicated Government here, by declaring us out of his Protection and waging War against us. 
+
+He has plundered our seas, ravaged our Coasts, burnt our towns, and destroyed the lives of our people. 
+
+He is at this time transporting large Armies of foreign Mercenaries to compleat the works of death, desolation and tyranny, already begun with circumstances of Cruelty and perfidy scarcely paralleled in the most barbarous ages, and totally unworthy the Head of a civilized nation. 
+
+He has constrained our fellow Citizens taken Captive on the high Seas to bear Arms against their Country, to become the executioners of their friends and Brethren, or to fall themselves by their Hands. 
+
+He has excited domestic insurrections amongst us, and has endeavoured to bring on the inhabitants of our frontiers, the merciless Indian Savages, whose known rule of warfare, is an undistinguished destruction of all ages, sexes and conditions. 
+
+In every stage of these Oppressions We have Petitioned for Redress in the most humble terms: Our repeated Petitions have been answered only by repeated injury. A Prince whose character is thus marked by every act which may define a Tyrant, is unfit to be the ruler of a free people. 
+
+Nor have We been wanting in attentions to our British brethren. We have warned them from time to time of attempts by their legislature to extend an unwarrantable jurisdiction over us. We have reminded them of the circumstances of our emigration and settlement here. We have appealed to their native justice and magnanimity, and we have conjured them by the ties of our common kindred to disavow these usurpations, which, would inevitably interrupt our connections and correspondence. They too have been deaf to the voice of justice and of consanguinity. We must, therefore, acquiesce in the necessity, which denounces our Separation, and hold them, as we hold the rest of mankind, Enemies in War, in Peace Friends. 
+
+We, therefore, the Representatives of the united States of America, in General Congress, Assembled, appealing to the Supreme Judge of the world for the rectitude of our intentions, do, in the Name, and by the Authority of the good People of these Colonies, solemnly publish and declare, That these United Colonies are, and of Right ought to be Free and Independent States; that they are Absolved from all Allegiance to the British Crown, and that all political connection between them and the State of Great Britain, is and ought to be totally dissolved; and that as Free and Independent States, they have full Power to levy War, conclude Peace, contract Alliances, establish Commerce, and to do all other Acts and Things which Independent States may of right do. And for the support of this Declaration, with a firm reliance on the protection of divine Providence, we mutually pledge to each other our Lives, our Fortunes and our sacred Honor. 
+
+The signers of the Declaration represented the new states as follows: 
+
+New Hampshire
+Josiah Bartlett, William Whipple, Matthew Thornton 
+
+Massachusetts
+John Hancock, Samual Adams, John Adams, Robert Treat Paine, Elbridge Gerry 
+
+Rhode Island
+Stephen Hopkins, William Ellery 
+
+Connecticut
+Roger Sherman, Samuel Huntington, William Williams, Oliver Wolcott 
+
+New York
+William Floyd, Philip Livingston, Francis Lewis, Lewis Morris 
+
+New Jersey
+Richard Stockton, John Witherspoon, Francis Hopkinson, John Hart, Abraham Clark 
+
+Pennsylvania
+Robert Morris, Benjamin Rush, Benjamin Franklin, John Morton, George Clymer, James Smith, George Taylor, James Wilson, George Ross 
+
+Delaware
+Caesar Rodney, George Read, Thomas McKean 
+
+Maryland
+Samuel Chase, William Paca, Thomas Stone, Charles Carroll of Carrollton 
+
+Virginia
+George Wythe, Richard Henry Lee, Thomas Jefferson, Benjamin Harrison, Thomas Nelson, Jr., Francis Lightfoot Lee, Carter Braxton 
+
+North Carolina
+William Hooper, Joseph Hewes, John Penn 
+
+South Carolina
+Edward Rutledge, Thomas Heyward, Jr., Thomas Lynch, Jr., Arthur Middleton 
+
+Georgia
+Button Gwinnett, Lyman Hall, George Walton 
diff --git a/data/make_dev_system.reg b/data/make_dev_system.reg
new file mode 100644
index 0000000..b845190
--- /dev/null
+++ b/data/make_dev_system.reg
Binary files differ
diff --git a/data/make_qa_system.reg b/data/make_qa_system.reg
new file mode 100644
index 0000000..855b8b4
--- /dev/null
+++ b/data/make_qa_system.reg
Binary files differ
diff --git a/data/manifest.xml b/data/manifest.xml
new file mode 100644
index 0000000..46f9e47
--- /dev/null
+++ b/data/manifest.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--Sample XML file generated by XMLSPY v2004 rel. 3 U (http://www.xmlspy.com)-->
+<Manifest ci:ver="1.0" ci:timestamp="aaaa" xmlns="http://www.google.com/ci/manifest" xmlns:ci="http://www.google.com/ci/manifest">
+  <App id="001" sig="abcd">
+    <Lang>EN-US</Lang>
+    <DisplayName>Google Toolbar</DisplayName>
+    <CreationDate>October 27, 2005</CreationDate>
+    <Ver>2.0</Ver>
+    <Description>Take the power of Google with you anywhere on the web.</Description>
+    <Logo>http://www.google.com/toolbar-logo.gif</Logo>
+    <MoreInfoUrl>http://toolbar.google.com</MoreInfoUrl>
+    <CodeBase>http://gpdl.google.com/GoogleEarth.exe</CodeBase>
+    <CodeBase file="setup.iss">http://gpdl.google.com/GoogleEarthSetup.iss</CodeBase>
+    <Size>476304</Size>
+    <Hash>JustTesting</Hash>
+    <CodeBase2>http://gpdl.google.com/GoogleEarthPatch.exe</CodeBase2>
+    <Size2>21391</Size2>
+    <Hash2>JustTesting2</Hash2>
+    <MinRequiredVersion>1.0</MinRequiredVersion>
+    <MinRecommendedVersion>1.0</MinRecommendedVersion>
+    <Versions>
+      <Version>
+        <ProductCode>HKLM\Software\Adobe\Acrobat Reader\7.0\Installer\ENU_GUID</ProductCode>
+        <AppPath>HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe\</AppPath>
+      </Version>
+      <Version>
+        <ProductCode>HKLM\Software\Adobe\Acrobat Reader\6.0\Installer\ENU_GUID</ProductCode>
+        <AppPath>HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe\</AppPath>
+      </Version>
+      <Version>
+        <ProductCode>Adobe Acrobat 5.0</ProductCode>
+        <AppPath>HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe\</AppPath>
+      </Version>
+      <Version>
+        <ProductCode>Adobe Acrobat 4.0</ProductCode>
+        <AppPath>HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe\</AppPath>
+      </Version>
+    </Versions>
+    <UsageStatsSwitch on="/u" off="/-u" />
+    <GoogleApp>1</GoogleApp>
+    <AltCommandLineParameters>-bundle=GCDR /nods /notb -silent /noshortcut</AltCommandLineParameters>
+    <AltVisualCommandLineParameters>-bundle=GCDR /nods /notb</AltVisualCommandLineParameters>
+    <WhatsNewUrl>http://earth.google.com/earth4.html</WhatsNewUrl>
+    <MinAutoupdateVersion>4.0.0.0</MinAutoupdateVersion>
+    <FlowMappings>
+      <Flow name="RegularPack">-bundle=GPCK /noshortcut</Flow>
+      <Flow name="EarthInt" replace="1">-bundle=GCDR</Flow>
+      <Flow name="OEM" replace="false">-o -r:%OEMBRAND%</Flow>
+    </FlowMappings>
+  </App>
+  <App id="002" sig="1234">
+    <Lang>En-US</Lang>
+    <DisplayName>Google Desktop Search</DisplayName>
+    <Ver>1.0</Ver>
+    <CreationDate>October 27, 2005</CreationDate>
+    <Description>Search your own computer.</Description>
+    <Logo>http://www.google.com/desktop-logo.gif</Logo>
+    <MoreInfoUrl>http://desktop.google.com</MoreInfoUrl>
+    <CodeBase>http://dl.google.com/desktop/GoogleDesktopSearchSetup.exe</CodeBase>
+    <Size>740456</Size>
+    <Hash>JustTesting</Hash>
+    <GoogleApp>false</GoogleApp> 
+  </App>
+</Manifest>
diff --git a/data/omaha.ini b/data/omaha.ini
new file mode 100644
index 0000000..fa4c1c6
--- /dev/null
+++ b/data/omaha.ini
@@ -0,0 +1,18 @@
+[LoggingLevel]
+LC_UTIL=0
+LC_SERVICE=3
+LC_CORE=3
+
+[LoggingSettings]
+EnableLogging=1
+ShowTime=1
+LogToFile=1
+AppendToFile=1
+LogToStdOut=0
+LogToOutputDebug=1
+LogFilePath=omaha.log
+
+[DebugSettings]
+SkipServerReport=1
+NoSendDumpToServer=1
+NoSendStackToServer=1
diff --git a/data/seed_manifest.xml b/data/seed_manifest.xml
new file mode 100644
index 0000000..cd5bbc9
--- /dev/null
+++ b/data/seed_manifest.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gupdate protocol="3.0" signature="" xmlns="http://www.google.com/update2/install">
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9937}" needsadmin="false" appname="Test App" lang="en-US"/>
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9938}" needsadmin="false" appname="Test App" lang="en-US" browser="0"/>
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9939}" needsadmin="false" appname="Test App" lang="en-US"  browser="1"/>
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9940}" needsadmin="false" appname="Test App" lang="en-US" browser="2"/>
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9941}" needsadmin="false" appname="Test App" lang="en-US" browser="3"/>
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9942}" needsadmin="false" appname="Test App" lang="en-US" browser="4"/>
+</gupdate>
diff --git a/data/seed_manifest_v1.xml b/data/seed_manifest_v1.xml
new file mode 100644
index 0000000..39011e5
--- /dev/null
+++ b/data/seed_manifest_v1.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<responses ver="1.0" signature="" xmlns="http://www.google.com/omaha/response">
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9937}" needsadmin="false"/>
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9938}" needsadmin="false"/>
+</responses>
diff --git a/data/seed_manifest_v9.xml b/data/seed_manifest_v9.xml
new file mode 100644
index 0000000..c10ec67
--- /dev/null
+++ b/data/seed_manifest_v9.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gupdate protocol="9.0" signature="" xmlns="http://www.google.com/update2/install">
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9937}" needsadmin="false" appname="Test App" language="en-US"/>
+<install appguid="{D6B08267-B440-4c85-9F79-E195E80D9938}" needsadmin="false" appname="Test App" language="en-US"/>
+</gupdate>
diff --git a/data/seed_manifest_with_args.xml b/data/seed_manifest_with_args.xml
new file mode 100644
index 0000000..19afafc
--- /dev/null
+++ b/data/seed_manifest_with_args.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gupdate protocol="2.0" signature="" xmlns="http://www.google.com/update2/install">
+  <install appguid="{283EAF47-8817-4c2b-A801-AD1FADFB7BAA}" needsadmin="true" iid="{874E4D29-8671-40C8-859F-4DECA4819999}" client="someclient" ap="1.0-dev"/>
+</gupdate>
diff --git a/data/seed_manifest_with_argsV3.xml b/data/seed_manifest_with_argsV3.xml
new file mode 100644
index 0000000..93b69dd
--- /dev/null
+++ b/data/seed_manifest_with_argsV3.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><gupdate protocol="3.0" signature="" xmlns="http://www.google.com/update2/install"><install appguid="{283EAF47-8817-4c2b-A801-AD1FADFB7BAA}" appname="Test App" lang="abcd" needsadmin="true" iid="{874E4D29-8671-40C8-859F-4DECA4819999}" rlz="hello" client="someclient" ap="1.0-dev"/></gupdate>
\ No newline at end of file
diff --git a/data/server_manifest.xml b/data/server_manifest.xml
new file mode 100644
index 0000000..0f1a903
--- /dev/null
+++ b/data/server_manifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
+<app appid="{D6B08267-B440-4C85-9F79-E195E80D9937}" status="ok">
+  <updatecheck status="ok"
+               codebase="http://dl.google.com/foo/1.0.101.0/test_foo_v1.0.101.0.msi"
+               size="80896"
+               hash="6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="
+               needsadmin="true"
+               successurl="http://testsuccessurl.com"
+               terminateallbrowsers="true"
+               onsuccess="exitsilently"/>
+  <data index="verboselogging" name="install" status="ok">
+  {
+    "distribution": {
+      "verbose_logging": true
+    }
+  }
+  </data>
+  <data index="skipfirstrun" name="install" status="ok">{
+    "distribution": {
+      "skip_first_run_ui": true,
+    }
+  }
+  </data>
+  <ping status="ok"/>
+</app>
+<app appid="{D6B08267-B440-4C85-9F79-E195E80D9936}" status="ok">
+  <updatecheck status="noupdate"/>
+  <ping status="ok"/>
+</app>
+<app appid="{104844D6-7DDA-460B-89F0-FBF8AFDD0A67}" status="ok">
+  <updatecheck status="ok"
+               codebase="http://dl.google.com/foo/1.0.102.0/user_foo_v1.0.102.0.msi"
+               size="630152"
+               hash="/XzRh1rpwqrDr6ashpmQnYZIzDI="
+               needsadmin="false"
+               arguments="/install"
+               unknownfeature="Tests behavior if server supports features client does not."/>
+  <ping status="ok"/>
+</app>
+<app appid="{884a01d9-fb67-430a-b491-28f960dd7309}" status="restricted"></app>
+<app appid="{8CF15C17-7BB5-433a-8E6C-C018D79D00B1}" status="ok">
+  <updatecheck status="error-osnotsupported"
+               errorurl="http://foo.google.com/support/article.py?id=12345&amp;hl=es-419&amp;os=5.1"/>
+  <ping status="ok"/>
+</app>
+</gupdate>
diff --git a/data/server_manifest_components.xml b/data/server_manifest_components.xml
new file mode 100644
index 0000000..4ce4bc1
--- /dev/null
+++ b/data/server_manifest_components.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
+  <app appid="{D6B08267-B440-4C85-9F79-E195E80D9937}" status="ok">
+    <updatecheck status="ok"
+                 codebase="http://dl.google.com/foo/1.0.101.0/test_foo_v1.0.101.0.msi"
+                 size="80896"
+                 hash="6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="
+                 needsadmin="true"
+                 successurl="http://testsuccessurl.com"
+                 terminateallbrowsers="true"
+                 onsuccess="exitsilently"/>
+    <ping status="ok"/>
+    <components>
+      <component appid="{65C42695-84A0-41C4-B70F-D2786F674592}" status="ok">
+        <updatecheck status="ok"
+                     codebase="http://dl.google.com/foo/set_comp1.msi"
+                     size="66324"
+                     hash="6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="/>
+      </component>
+      <component appid="{B318029C-3607-48EB-8DBB-33E8BA17BAF1}" status="noupdate"/>
+      <component appid="{D76AE6FC-1633-4131-B782-896804795DCB}" status="ok">
+        <updatecheck status="ok"
+                     codebase="http://tools.google.com/happy/some_comp_inst.msi"
+                     size="829984"
+                     hash="6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="/>
+      </component>
+      <component appid="{67A52AEE-6E9F-4411-B425-F210B962CD6F}" status="noupdate"/>
+    </components>
+  </app>
+  <app appid="{D6B08267-B440-4C85-9F79-E195E80D9936}" status="ok">
+    <updatecheck status="noupdate"/>
+    <components>
+      <component appid="{115493D4-6B38-4314-81B7-26B61F1766A9}" status="ok">
+        <updatecheck status="ok"
+                     codebase="http://foo.google.com/bar/something.exe"
+                     size="12345"
+                     hash="some_hash_value"/>
+      </component>
+    </components>
+    <rlz status="ok"/>
+    <ping status="ok"/>
+  </app>
+  <app appid="{104844D6-7DDA-460B-89F0-FBF8AFDD0A67}" status="ok">
+    <updatecheck status="ok"
+                 codebase="http://dl.google.com/foo/1.0.102.0/user_foo_v1.0.102.0.msi"
+                 size="630152"
+                 hash="/XzRh1rpwqrDr6ashpmQnYZIzDI="
+                 needsadmin="false"
+                 arguments="/install"/>
+    <rlz parameter="1O1GGLGN__"/>
+    <ping status="ok"/>
+  </app>
+  <app appid="{884a01d9-fb67-430a-b491-28f960dd7309}" status="restricted"></app>
+</gupdate>
+
diff --git a/data/server_manifest_one_app.xml b/data/server_manifest_one_app.xml
new file mode 100644
index 0000000..3b3fd6b
--- /dev/null
+++ b/data/server_manifest_one_app.xml
@@ -0,0 +1,21 @@
+<?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 status="ok"
+               codebase="http://dl.google.com/foo/1.0.101.0/foo_installer.exe"
+               size="82128"
+               hash="U4gBMUVwIPQners8SriHpHaWEKk="
+               needsadmin="true"
+               successurl="http://testsuccessurl.com"
+               onsuccess="exitsilentlyonlaunchcmd"
+               terminateallbrowsers="true"/>
+  <data index="verboselogging" name="install" status="ok">
+  {
+    "distribution": {
+      "verbose_logging": true
+    }
+  }
+  </data>
+  <ping status="ok"/>
+</app>
+</gupdate>
diff --git a/enterprise/__init__.py b/enterprise/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/enterprise/__init__.py
diff --git a/enterprise/build.scons b/enterprise/build.scons
new file mode 100644
index 0000000..4389722
--- /dev/null
+++ b/enterprise/build.scons
@@ -0,0 +1,46 @@
+#!/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 os
+from enterprise import public_apps
+from enterprise import generate_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'])
+
+env.BuildSConscript('installer')
diff --git a/enterprise/const_group_policy.h b/enterprise/const_group_policy.h
new file mode 100644
index 0000000..d639673
--- /dev/null
+++ b/enterprise/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_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
new file mode 100644
index 0000000..2707a0c
--- /dev/null
+++ b/enterprise/generate_group_policy_template.py
@@ -0,0 +1,377 @@
+#!/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.
+# ========================================================================
+
+"""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).
+
+To unit test this module, just run the file from the command line.
+"""
+
+import filecmp
+import os
+import sys
+
+
+HORIZONTAL_RULE = ';%s\n' % ('-' * 78)
+
+# pylint: disable-msg=C6004
+HEADER = """\
+CLASS MACHINE
+  CATEGORY !!Cat_Google
+    CATEGORY !!Cat_GoogleUpdate
+      KEYNAME !!Key_BaseName
+      EXPLAIN !!Explain_GoogleUpdate
+"""
+
+PREFERENCES = """
+      CATEGORY !!Cat_Preferences
+        KEYNAME !!Key_BaseName
+        EXPLAIN !!Explain_Preferences
+
+        POLICY !!Pol_AutoUpdateCheckPeriod
+          EXPLAIN !!Explain_AutoUpdateCheckPeriod
+          PART !!Part_AutoUpdateCheckPeriod NUMERIC
+            VALUENAME AutoUpdateCheckPeriodMinutes
+            DEFAULT 1400  ; 23 hours 20 minutes.
+            MIN 30
+            MAX 65535     ; 45.5 days.
+            SPIN 60       ; Increment in hour chunks.
+          END PART
+        END POLICY
+
+      END CATEGORY  ; Preferences
+"""
+
+APPLICATIONS_HEADER = """
+      CATEGORY !!Cat_Applications
+        KEYNAME !!Key_BaseName
+        EXPLAIN !!Explain_Applications
+"""
+
+UPDATE_POLICY_ITEMLIST = """\
+            ITEMLIST
+              NAME  !!Name_AutomaticUpdates
+              VALUE NUMERIC 1
+              NAME  !!Name_ManualUpdates
+              VALUE NUMERIC 2
+              NAME  !!Name_UpdatesDisabled
+              VALUE NUMERIC 0
+            END ITEMLIST
+            REQUIRED"""
+
+APPLICATION_DEFAULTS = ("""
+        POLICY !!Pol_DefaultAllowInstallation
+          EXPLAIN !!Explain_DefaultAllowInstallation
+          VALUENAME InstallDefault
+          VALUEOFF  NUMERIC 0
+          VALUEON   NUMERIC 1
+        END POLICY
+
+        POLICY !!Pol_DefaultUpdatePolicy
+          EXPLAIN !!Explain_DefaultUpdatePolicy
+          PART !!Part_UpdatePolicy DROPDOWNLIST
+            VALUENAME UpdateDefault
+""" +
+UPDATE_POLICY_ITEMLIST + """
+          END PART
+        END POLICY
+""")
+
+APP_POLICIES_TEMPLATE = ("""
+        CATEGORY !!Cat_$AppLegalId$
+          KEYNAME !!Key_BaseName
+
+          POLICY !!Pol_AllowInstallation
+            EXPLAIN !!Explain_Install$AppLegalId$
+            VALUENAME Install$AppGuid$
+            VALUEOFF  NUMERIC 0
+            VALUEON   NUMERIC 1
+          END POLICY
+
+          POLICY !!Pol_UpdatePolicy
+            EXPLAIN !!Explain_AutoUpdate$AppLegalId$
+            PART !!Part_UpdatePolicy DROPDOWNLIST
+              VALUENAME Update$AppGuid$
+""" +
+UPDATE_POLICY_ITEMLIST.replace('            ', '              ') + """
+            END PART
+          END POLICY
+
+        END CATEGORY  ; $AppName$
+""")
+
+APPLICATIONS_FOOTER = """
+      END CATEGORY  ; Applications
+
+    END CATEGORY  ; GoogleUpdate
+
+  END CATEGORY  ; Google
+"""
+
+# Policy names that are used in multiple locations.
+ALLOW_INSTALLATION_POLICY = 'Allow installation'
+DEFAULT_ALLOW_INSTALLATION_POLICY = ALLOW_INSTALLATION_POLICY + ' default'
+UPDATE_POLICY = 'Update policy override'
+DEFAULT_UPDATE_POLICY = UPDATE_POLICY + ' default'
+
+# Update policy options that are used in multiple locations.
+AUTOMATIC_UPDATES = 'Automatic silent updates'
+MANUAL_UPDATES = 'Manual updates only'
+UPDATES_DISABLED = 'Updates disabled'
+
+# Category names that are used in multiple locations.
+PREFERENCES_CATEGORY = 'Preferences'
+APPLICATIONS_CATEGORY = 'Applications'
+
+# The captions for update policy were selected such that they appear in order of
+# decreasing preference when organized alphabetically in gpedit.
+STRINGS_HEADER_AND_COMMON = ('\n' +
+HORIZONTAL_RULE +
+"""
+[strings]
+Key_BaseName=Software\\Policies\\Google\\Update
+
+Cat_Google=Google
+Cat_GoogleUpdate=Google Update
+Cat_Preferences=""" + PREFERENCES_CATEGORY + """
+Cat_Applications=""" + APPLICATIONS_CATEGORY + """
+
+Pol_AutoUpdateCheckPeriod=Auto-update check period override
+Pol_DefaultAllowInstallation=""" + DEFAULT_ALLOW_INSTALLATION_POLICY + """
+Pol_AllowInstallation=""" + ALLOW_INSTALLATION_POLICY + """
+Pol_DefaultUpdatePolicy=""" + DEFAULT_UPDATE_POLICY + """
+Pol_UpdatePolicy=""" + UPDATE_POLICY + """
+
+Part_AutoUpdateCheckPeriod=Minutes between update checks
+Part_UpdatePolicy=Policy
+
+Name_AutomaticUpdates=""" + AUTOMATIC_UPDATES + """ (recommended)
+Name_ManualUpdates=""" + MANUAL_UPDATES + """
+Name_UpdatesDisabled=""" + UPDATES_DISABLED + """
+
+""")
+
+STRINGS_APP_NAME_TEMPLATE = """\
+Cat_$AppLegalId$=$AppName$
+"""
+
+# pylint: disable-msg=C6310
+# pylint: disable-msg=C6013
+
+# "application's" should be preceeded by a different word in different contexts.
+# The word is specified by replacing the $PreApplicationWord$ token.
+STRINGS_UPDATE_POLICY_OPTIONS = """\
+    \\n\\nOptions:\\
+    \\n - """ + AUTOMATIC_UPDATES + """: Updates are automatically applied when they are found via the periodic update check.\\
+    \\n - """ + MANUAL_UPDATES + """: Updates are only applied when the user does a manual update check. (Not all apps provide an interface for this.)\\
+    \\n - """ + UPDATES_DISABLED + """: Never apply updates.\\
+    \\n\\nIf you select manual updates, you should periodically check for updates using $PreApplicationWord$ application's manual update mechanism if available. If you disable updates, you should periodically check for updates and distribute them to users."""
+
+STRINGS_COMMON_EXPLANATIONS = ("""
+Explain_GoogleUpdate=Policies to control the installation and updating of Google applications that use Google Update/Google Installer.
+
+""" +
+HORIZONTAL_RULE +
+'; ' + PREFERENCES_CATEGORY + '\n' +
+HORIZONTAL_RULE + """
+Explain_Preferences=General policies for Google Update.
+
+Explain_AutoUpdateCheckPeriod=Minimum number of minutes between automatic update checks.
+
+""" +
+HORIZONTAL_RULE +
+'; ' + APPLICATIONS_CATEGORY + '\n' +
+HORIZONTAL_RULE + """
+Explain_Applications=Policies for individual applications.\\
+    \\n\\nAn updated ADM template will be required to support Google applications released in the future.
+
+Explain_DefaultAllowInstallation=Specifies the default behavior for whether Google software can be installed using Google Update/Google Installer.\\
+    \\n\\nCan be overridden by the \"""" + ALLOW_INSTALLATION_POLICY + """\" for individual applications.\\
+    \\n\\nOnly affects installation of Google software using Google Update/Google Installer. Cannot prevent running the application installer directly or installation of Google software that does not use Google Update/Google Installer for installation.
+
+Explain_DefaultUpdatePolicy=Specifies the default policy for software updates from Google.\\
+    \\n\\nCan be overridden by the \"""" + UPDATE_POLICY + """\" for individual applications.\\
+""" +
+STRINGS_UPDATE_POLICY_OPTIONS.replace('$PreApplicationWord$', 'each') + """\\
+    \\n\\nOnly affects updates for Google software that uses Google Update for updates. Does not prevent auto-updates of Google software that does not use Google Update for updates.\\
+    \\n\\nUpdates for Google Update are not affected by this setting; Google Update will continue to update itself while it is installed.\\
+    \\n\\nWARNING: Disabing updates will also prevent updates of any new Google applications released in the future, possibly including dependencies for future versions of installed applications.
+
+""" +
+HORIZONTAL_RULE +
+'; Individual Applications\n' +
+HORIZONTAL_RULE)
+
+STRINGS_APP_POLICY_EXPLANATIONS_TEMPLATE = ("""
+; $AppName$
+Explain_Install$AppLegalId$=Specifies whether $AppName$ can be installed using Google Update/Google Installer.\\
+    \\n\\nIf this policy is not configured, $AppName$ can be installed as specified by \"""" + DEFAULT_ALLOW_INSTALLATION_POLICY + """\".
+
+Explain_AutoUpdate$AppLegalId$=Specifies how Google Update handles available $AppName$ updates from Google.\\
+    \\n\\nIf this policy is not configured, Google Update handles available updates as specified by \"""" + DEFAULT_UPDATE_POLICY + """\".\\
+""" +
+STRINGS_UPDATE_POLICY_OPTIONS.replace('$PreApplicationWord$', 'the') + '$AppUpdateExplainExtra$\n')
+
+# pylint: enable-msg=C6013
+# pylint: enable-msg=C6310
+# pylint: enable-msg=C6004
+
+
+def GenerateGroupPolicyTemplate(apps):
+  """Generates a Group Policy template (ADM format)for the specified apps.
+
+  Args:
+    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).
+    target_path: Output path of the .ADM template file.
+
+  Returns:
+    String containing the contents of the .ADM file.
+  """
+
+  def _CreateLegalIdentifier(input_string):
+    """Converts input_string to a legal identifier for ADM files.
+
+    Changes some characters that do not necessarily cause problems and may not
+    handle all cases.
+
+    Args:
+      input_string: Text to convert to a legal identifier.
+
+    Returns:
+      String containing a legal identifier based on input_string.
+    """
+
+    # pylint: disable-msg=C6004
+    return (input_string.replace(' ', '')
+                        .replace('&', '')
+                        .replace('=', '')
+                        .replace(';', '')
+                        .replace(',', '')
+                        .replace('.', '')
+                        .replace('?', '')
+                        .replace('=', '')
+                        .replace(';', '')
+                        .replace("'", '')
+                        .replace('"', '')
+                        .replace('\\', '')
+                        .replace('/', '')
+                        .replace('(', '')
+                        .replace(')', '')
+                        .replace('[', '')
+                        .replace(']', '')
+                        .replace('{', '')
+                        .replace('}', '')
+                        .replace('-', '')
+                        .replace('!', '')
+                        .replace('@', '')
+                        .replace('#', '')
+                        .replace('$', '')
+                        .replace('%', '')
+                        .replace('^', '')
+                        .replace('*', '')
+                        .replace('+', ''))
+    # pylint: enable-msg=C6004
+
+  def _WriteTemplateForApp(template, app):
+    """Writes the text for the specified app based on the template.
+
+    Replaces $AppName$, $AppLegalId$, $AppGuid$, and $AppUpdateExplainExtra$.
+
+    Args:
+      template: text to process and write.
+      app: tuple containing information about the app.
+
+    Returns:
+      String containing a copy of the template populated with app-specific
+      strings.
+    """
+
+    (app_name, app_guid, update_explain_extra) = app
+    # pylint: disable-msg=C6004
+    return (template.replace('$AppName$', app_name)
+                    .replace('$AppLegalId$', _CreateLegalIdentifier(app_name))
+                    .replace('$AppGuid$', app_guid)
+                    .replace('$AppUpdateExplainExtra$', update_explain_extra)
+           )
+    # pylint: enable-msg=C6004
+
+  def _WriteTemplateForAllApps(template, apps):
+    """Writes a copy of the template for each of the specified apps.
+
+    Args:
+      template: text to process and write.
+      apps: list of tuples containing information about the apps.
+
+    Returns:
+      String containing concatenated copies of the template for each app in
+      apps, each populated with the appropriate app-specific strings.
+    """
+
+    content = [_WriteTemplateForApp(template, app) for app in apps]
+    return ''.join(content)
+
+  target_contents = [
+      HEADER,
+      PREFERENCES,
+      APPLICATIONS_HEADER,
+      APPLICATION_DEFAULTS,
+      _WriteTemplateForAllApps(APP_POLICIES_TEMPLATE, apps),
+      APPLICATIONS_FOOTER,
+      STRINGS_HEADER_AND_COMMON,
+      _WriteTemplateForAllApps(STRINGS_APP_NAME_TEMPLATE, apps),
+      STRINGS_COMMON_EXPLANATIONS,
+      _WriteTemplateForAllApps(STRINGS_APP_POLICY_EXPLANATIONS_TEMPLATE, apps),
+      ]
+
+  return ''.join(target_contents)
+
+
+# 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/.'),
+      ]
+  TEST_GOLD_FILENAME = 'test_gold.adm'
+  TEST_OUTPUT_FILENAME = 'test_out.adm'
+
+  module_dir = os.path.abspath(os.path.dirname(__file__))
+  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()
+
+  if filecmp.cmp(gold_path, output_path, shallow=False):
+    print 'PASS: Contents equal.'
+  else:
+    print 'FAIL: Contents not equal.'
+    sys.exit(-1)
diff --git a/enterprise/installer/__init__.py b/enterprise/installer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/enterprise/installer/__init__.py
diff --git a/enterprise/installer/build.scons b/enterprise/installer/build.scons
new file mode 100644
index 0000000..e32e24e
--- /dev/null
+++ b/enterprise/installer/build.scons
@@ -0,0 +1,134 @@
+#!/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.
+# ========================================================================
+
+""" Build enterprise installers examples.
+
+  This file contains the product-specific information for building an
+  MSI for remote installation in the enterprise. Product teams should only
+  need to modify this file.
+  APPTEAMS:
+  There are two ways to build such an MSI depending on whether the product's
+  installer is or contains an MSI.
+"""
+
+Import('env')
+
+from enterprise.installer import build_enterprise_installer
+
+
+# APPTEAMS: Change this value to False.
+# Build the installer using build rules specific to Google Update builds.
+BUILDING_WITH_GOOGLE_UPDATE = True
+
+# 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).
+
+# APPTEAMS: May be able to use this to get the version instead of explicitly
+# specifying it in the table below.
+# v = env['product_version'][0]
+# PRODUCT_VERSION = '%d.%d.%d.%d' % (v[0], v[1], v[2], v[3])
+
+#
+# Applications with EXE installers
+#
+# Use this section to build an MSI that contains Google Update and a product's
+# EXE installer. The EXE may not use an MSI installer internally.
+# MSI installers cannot be wrapped in this way because nested MSI installs are
+# discouraged, problematic, and deprecated from WiX.
+
+# APPTEAMS: Make appropriate call to BuildEnterpriseInstaller(). Example below.
+
+# This sample:
+#  * Builds multiple "products" (each product is actually a different version).
+#    Products are specified in an array of structures, which specify the values
+#    for each product.
+#  * Builds multiple pre-tagged brands and ap values for each "product."
+def _BuildSampleForTest():
+  ENTERPRISE_INSTALLER_PRODUCTS = [
+    ('Save Arguments', '0.1.2.0', '{7DD1EF7B-D075-47c0-BD51-F624ED87CCF0}',
+     '&brand=mkfi',
+     '$MAIN_DIR/testing/unittest_support/SaveArguments.exe',
+     '/install 0.1.2.0', '/donotregister', '/uninstall 0.1.2.0',
+      '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', '/uninstall 0.1.3.0',
+     'SaveArguments_Enterprise_Installer_0.1.3.0'),
+    ]
+
+  # Example of calling BuildEnterpriseInstaller with multiple brands and ap
+  # value combinations for each product.
+  BRAND_AP_COMBINATIONS = [
+    ('FOOB', 'foobar'),
+    #('ENTA', 'enterprise'),
+    #('ENTB', 'enterprise'),
+  ]
+
+  # Omaha only: Build the enterprise installer for each version of Omaha.
+  first = True
+  for _ in env['product_version']:
+    if first:
+      first = False
+      prefix = ''
+    else:
+      prefix = 'TEST_'
+
+    for combo in BRAND_AP_COMBINATIONS:
+      for product in ENTERPRISE_INSTALLER_PRODUCTS:
+        (product_name, product_version, product_guid,
+         product_custom_params,
+         product_installer_path,
+         product_installer_install_command,
+         product_installer_disable_update_registration_arg,
+         product_installer_uninstall_command,
+         msi_base_name) = product
+
+        product_custom_params = '&brand=' + combo[0] + '&ap=' + combo[1]
+
+        build_enterprise_installer.BuildEnterpriseInstaller(
+            env,
+            product_name, product_version, product_guid,
+            product_custom_params,
+            product_installer_path,
+            product_installer_install_command,
+            product_installer_disable_update_registration_arg,
+            product_installer_uninstall_command,
+            prefix + msi_base_name + '_' + combo[0],
+            '$MAIN_DIR/enterprise/installer',
+            '$STAGING_DIR/%sGoogleUpdateSetup.exe' % prefix
+        )
+
+if BUILDING_WITH_GOOGLE_UPDATE:
+  _BuildSampleForTest()
+
+
+#
+# Applications with MSI installers
+#
+# To bundle Google Update with a product that has an MSI installer:
+#  * Build a .wixobj that contains the Google Update installation fragment
+#    by calling BuildGoogleUpdateFragment().
+#  * Include the ComponentGoogleUpdate component in the appropriate feature in
+#    the product's WiX code.
+#
+# For an example, see instances of "enterprise" in the following files:
+#  omaha/test/test_foo.wxs.xml
+#  omaha/test/build.scons
diff --git a/enterprise/installer/build_enterprise_installer.py b/enterprise/installer/build_enterprise_installer.py
new file mode 100644
index 0000000..2d52aba
--- /dev/null
+++ b/enterprise/installer/build_enterprise_installer.py
@@ -0,0 +1,277 @@
+#!/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.
+# ========================================================================
+
+"""Build an installer for use in enterprise situations.
+
+  This module contains the functionality required to build enterprise
+  installers (msi's) for Omaha's various customers.
+
+  The supplied wxs templates need to have an XML extension because SCons
+  tries to apply WiX building rules to any input file with the .wxs suffix.
+
+  BuildGoogleUpdateFragment(): Build an update fragment into a .wixobj.
+  GenerateNameBasedGUID(): Generate a GUID based on the names supplied.
+  BuildEnterpriseInstaller(): Build an msi installer for use in enterprises.
+"""
+
+import binascii
+import md5
+
+
+def BuildGoogleUpdateFragment(env,
+                              metainstaller_path,
+                              product_name,
+                              product_guid,
+                              product_custom_params,
+                              wixobj_base_name,
+                              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.
+
+  Args:
+    env: environment to build with
+    metainstaller_path: path to the Google Update metainstaller to include
+    product_name: name of the product the fragment is being built for
+    product_guid: Google Update application ID of the product the fragment is
+        being built for
+    product_custom_params: custom values to be appended to the Google Update tag
+    wixobj_base_name: root of name for the wixobj
+    google_update_wxs_template_path: path to the fragment source
+
+  Returns:
+    Output object for the built wixobj.
+
+  Raises:
+    Nothing.
+  """
+
+  product_name_legal_identifier = product_name.replace(' ', '')
+
+  intermediate_base_name = wixobj_base_name + '_google_update_fragment'
+
+  copy_target = env.Command(
+      target=intermediate_base_name + '.wxs',
+      source=google_update_wxs_template_path,
+      action='@copy /y $SOURCE $TARGET',
+  )
+
+  wix_defines = [
+      '-dProductName="%s"' % product_name,
+      '-dProductNameLegalIdentifier="%s"' % product_name_legal_identifier,
+      '-dProductGuid="%s"' % product_guid,
+      '-dProductCustomParams="%s"' % product_custom_params,
+      '-dGoogleUpdateMetainstallerPath="%s"' % (
+          env.File(metainstaller_path).abspath),
+      ]
+
+  wixobj_output = env.Command(
+      target=intermediate_base_name + '.wixobj',
+      source=copy_target,
+      action='@candle.exe -nologo -out $TARGET $SOURCE ' + ' '.join(wix_defines)
+  )
+
+  # Force a rebuild of the .wixobj file when the metainstaller changes.
+  # Does not necessarily force rebuild of the MSI because hash does not change.
+  env.Depends(wixobj_output, metainstaller_path)
+
+  return wixobj_output
+
+
+def _BuildMsiForExe(env,
+                    enterprise_installer_dir,
+                    product_name,
+                    product_version,
+                    product_installer_path,
+                    product_installer_install_command,
+                    product_installer_disable_update_registration_arg,
+                    product_installer_uninstall_command,
+                    msi_base_name,
+                    google_update_wixobj_output,
+                    metainstaller_path):
+  # metainstaller_path: path to the Google Update metainstaller. Should be same
+  #     file used for google_update_wixobj_output. Used only to force rebuilds.
+
+  product_name_legal_identifier = product_name.replace(' ', '')
+  msi_name = msi_base_name + '.msi'
+
+  gu_installer_namespace = binascii.a2b_hex('BE19B3E4502845af8B3E67A99FCDCFB1')
+
+  # Include the .msi filename in the Product Code generation because "the
+  # product code must be changed if... the name of the .msi file has been
+  # changed" according to http://msdn.microsoft.com/en-us/library/aa367850.aspx.
+  msi_product_id = GenerateNameBasedGUID(
+      gu_installer_namespace,
+      'Product %s %s' % (product_name, msi_base_name)
+  )
+  msi_upgradecode_guid = GenerateNameBasedGUID(
+      gu_installer_namespace,
+      'Upgrade ' + product_name
+  )
+
+  copy_target = env.Command(
+      target= msi_base_name + '.wxs',
+      source=enterprise_installer_dir + '/enterprise_installer.wxs.xml',
+      action='@copy /y $SOURCE $TARGET',
+  )
+
+  wix_env = env.Clone()
+  wix_env.Append(
+      WIXCANDLEFLAGS = [
+          '-dProductName=' + product_name,
+          '-dProductNameLegalIdentifier=' + product_name_legal_identifier,
+          '-dProductVersion=' + product_version,
+          '-dProductInstallerPath=' + env.File(product_installer_path).abspath,
+          '-dProductInstallerInstallCommand=' +
+              product_installer_install_command,
+          '-dProductInstallerDisableUpdateRegistrationArg=' +
+              product_installer_disable_update_registration_arg,
+          '-dProductInstallerUninstallCommand=' +
+              product_installer_uninstall_command,
+          '-dMsiProductId=' + msi_product_id,
+          '-dMsiUpgradeCode=' + msi_upgradecode_guid,
+          ],
+  )
+
+  wix_output = wix_env.WiX(
+      target='unsigned_' + msi_name,
+      source=[copy_target, google_update_wixobj_output],
+  )
+
+  # Force a rebuild when the installer or metainstaller changes.
+  # The metainstaller change does not get passed through even though the .wixobj
+  # file is rebuilt because the hash of the .wixobj does not change.
+  wix_env.Depends(wix_output,
+                  [product_installer_path, metainstaller_path])
+
+  sign_output = wix_env.SignedBinary(
+      target=msi_name,
+      source=wix_output,
+  )
+
+  env.Replicate('$STAGING_DIR', sign_output)
+
+
+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.
+
+    Args:
+      namespace: First part of identifier used to generate GUID
+      name: Second part of identifier used to generate GUID
+
+    Returns:
+      String representation of the generated GUID.
+
+    Raises:
+      Nothing.
+  """
+
+  # Generate 128 unique bits.
+  mymd5 = md5.new()
+  mymd5.update(namespace + name)
+  hash = mymd5.digest()
+
+  # Set various reserved bits to make this a valid GUID.
+
+  # "Set the four most significant bits (bits 12 through 15) of the
+  # time_hi_and_version field to the appropriate 4-bit version number
+  # from Section 4.1.3."
+  version = ord(hash[6])
+  version = 0x30 | (version & 0x0f)
+
+  # "Set the two most significant bits (bits 6 and 7) of the
+  # clock_seq_hi_and_reserved to zero and one, respectively."
+  clock_seq_hi_and_reserved = ord(hash[8])
+  clock_seq_hi_and_reserved = 0x80 | (clock_seq_hi_and_reserved & 0x3f)
+
+  return (
+      '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % (
+      ord(hash[0]), ord(hash[1]), ord(hash[2]), ord(hash[3]), ord(hash[4]),
+      ord(hash[5]), version, ord(hash[7]), clock_seq_hi_and_reserved,
+      ord(hash[9]), ord(hash[10]), ord(hash[11]), ord(hash[12]), ord(hash[13]),
+      ord(hash[14]), ord(hash[15])))
+
+
+def BuildEnterpriseInstaller(env,
+                             product_name,
+                             product_version,
+                             product_guid,
+                             product_custom_params,
+                             product_installer_path,
+                             product_installer_install_command,
+                             product_installer_disable_update_registration_arg,
+                             product_installer_uninstall_command,
+                             msi_base_name,
+                             enterprise_installer_dir,
+                             metainstaller_path):
+  """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.
+
+  Args:
+    env: environment to build with
+    product_name: name of the product being built
+    product_version: product version to be installed
+    product_guid: product's Google Update application ID
+    product_custom_params: custom values to be appended to the Google Update tag
+    product_installer_path: path to specific product installer
+    product_installer_install_command: command line used to run product
+        installer in 'install' mode
+    product_installer_disable_update_registration_arg: command line used
+        to run product installer in 'do not register' mode
+    product_installer_uninstall_command: command line used to run product
+        installer in 'uninstall' mode
+    msi_base_name: root of name for the msi
+    enterprise_installer_dir: path to dir which contains
+        enterprise_installer.wxs.xml
+    metainstaller_path: path to the Google Update metainstaller to include
+
+  Returns:
+    Nothing.
+
+  Raises:
+    Nothing.
+  """
+
+  google_update_wixobj_output = BuildGoogleUpdateFragment(
+      env,
+      metainstaller_path,
+      product_name,
+      product_guid,
+      product_custom_params,
+      msi_base_name,
+      enterprise_installer_dir + '/google_update_installer_fragment.wxs.xml')
+
+  _BuildMsiForExe(
+      env,
+      enterprise_installer_dir,
+      product_name,
+      product_version,
+      product_installer_path,
+      product_installer_install_command,
+      product_installer_disable_update_registration_arg,
+      product_installer_uninstall_command,
+      msi_base_name,
+      google_update_wixobj_output,
+      metainstaller_path)
diff --git a/enterprise/installer/enterprise_installer.wxs.xml b/enterprise/installer/enterprise_installer.wxs.xml
new file mode 100644
index 0000000..fbaef86
--- /dev/null
+++ b/enterprise/installer/enterprise_installer.wxs.xml
@@ -0,0 +1,131 @@
+<?xml version='1.0' encoding='windows-1252'?>
+<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
+  <?define CompanyFullName = 'Google, Inc.'?>
+  <?define Copyright = 'Copyright 2007-2009 Google Inc.'?>
+
+  <Product
+    Id='$(var.MsiProductId)'
+    Name='$(var.ProductName)'
+    Language='1033'
+    Codepage='1252'
+    UpgradeCode='$(var.MsiUpgradeCode)'
+    Version='$(var.ProductVersion)'
+    Manufacturer='$(var.CompanyFullName)'>
+
+    <Package
+      Id='*'
+      Description='$(var.ProductName) Installer'
+      Comments='$(var.Copyright)'
+      Manufacturer='$(var.CompanyFullName)'
+      Languages='1033'
+      SummaryCodepage='1252'
+      InstallerVersion='150'
+      InstallPrivileges='elevated'
+      Compressed='yes' />
+
+    <Upgrade Id='$(var.MsiUpgradeCode)'>
+      <UpgradeVersion Property='UPGRADING'
+                      OnlyDetect='no'
+                      Minimum='0.0.0.0' IncludeMinimum='yes'
+                      Maximum='$(var.ProductVersion)' IncludeMaximum='no' />
+      <UpgradeVersion Property='NEWERVERSIONDETECTED' 
+                      OnlyDetect='yes'
+                      Minimum='$(var.ProductVersion)' IncludeMinimum='yes' />
+    </Upgrade>
+
+    <!-- Per-machine installation - make sure product appears for all users. -->
+    <Property Id='ALLUSERS' Value='1' />
+
+    <!-- Hide Add/Remove Programs entry -->
+    <Property Id='ARPSYSTEMCOMPONENT' Value='1' />
+
+    <UI>
+      <Error Id='4000'>A newer version of the $(var.ProductName) enterprise installer is already installed.</Error>
+    </UI>
+
+    <!-- Eliminates "warning LGHT1076 : ICE71: The Media table has no entries."
+    -->
+    <Media Id='1' />
+
+    <Directory Id='TARGETDIR' Name='SourceDir'>
+      <Directory Id='ProgramFilesFolder'>
+      </Directory>
+    </Directory>
+
+
+    <Binary Id='$(var.ProductNameLegalIdentifier)Installer'
+            SourceFile='$(var.ProductInstallerPath)' />
+
+
+    <Feature Id='Complete' Level='1'>
+      <ComponentRef Id='ComponentGoogleUpdate' />
+    </Feature>
+
+    <CustomAction Id='NewerVersionError' Error='4000'/>
+
+    <CustomAction Id='SetInstallerInstallCommandProperty'
+      Property='FullProductInstallerInstallCommand'
+      Value='$(var.ProductInstallerInstallCommand)'
+      Execute='immediate'
+      Return='check' />
+    <CustomAction 
+      Id='AppendDisableUpdateRegistrationArgToInstallerInstallCommandProperty'
+      Property='FullProductInstallerInstallCommand'
+      Value='[FullProductInstallerInstallCommand]
+             $(var.ProductInstallerDisableUpdateRegistrationArg)'
+      Execute='immediate'
+      Return='check' />
+    <CustomAction Id='Install$(var.ProductNameLegalIdentifier)'
+      BinaryKey='$(var.ProductNameLegalIdentifier)Installer'
+      Impersonate='no'
+      Execute='deferred'
+      ExeCommand='[FullProductInstallerInstallCommand]'
+      Return='check' />
+    <CustomAction Id='Uninstall$(var.ProductNameLegalIdentifier)'
+      BinaryKey='$(var.ProductNameLegalIdentifier)Installer'
+      Impersonate='no'
+      Execute='deferred'
+      ExeCommand='$(var.ProductInstallerUninstallCommand)'
+      Return='check' />
+
+    <InstallExecuteSequence>
+      <RemoveExistingProducts After='InstallValidate' />
+
+      <Custom Action='NewerVersionError' After='FindRelatedProducts'>
+        NEWERVERSIONDETECTED
+      </Custom>
+
+      <!-- Any operations that rely on values, such as brand and usagestats, in
+           ClientState should not run until after
+           InstallGoogleUpdateAndRegister. This is sequenced before InstallFiles
+           so that can be used as well. -->
+      <!-- TODO(omaha): Support Rollback? Need to determine whether Product
+           was installed before installing it and only add the rollback then.
+      <Custom Action='Rollback$(var.ProductNameLegalIdentifier)'
+              After='InstallFiles'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL) AND (NOT $(var.ProductNameLegalIdentifier)_INSTALLED)
+      </Custom>
+      Change After='InstallFiles' below to
+      After='Rollback$(var.ProductNameLegalIdentifier)'. -->
+      <Custom Action='SetInstallerInstallCommandProperty'
+              After='InstallFiles'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL)
+      </Custom>
+      <Custom Action='AppendDisableUpdateRegistrationArgToInstallerInstallCommandProperty'
+              After='SetInstallerInstallCommandProperty'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL) AND DISABLE_UPDATES
+      </Custom>
+      <Custom Action='Install$(var.ProductNameLegalIdentifier)'
+              After='AppendDisableUpdateRegistrationArgToInstallerInstallCommandProperty'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL)
+      </Custom>
+
+      <Custom Action='Uninstall$(var.ProductNameLegalIdentifier)'
+              Before='RemoveFiles'>
+        $ComponentGoogleUpdate=2
+      </Custom>
+
+    </InstallExecuteSequence>
+
+  </Product>
+</Wix>
diff --git a/enterprise/installer/google_update_installer_fragment.wxs.xml b/enterprise/installer/google_update_installer_fragment.wxs.xml
new file mode 100644
index 0000000..612f139
--- /dev/null
+++ b/enterprise/installer/google_update_installer_fragment.wxs.xml
@@ -0,0 +1,100 @@
+<?xml version='1.0' encoding='windows-1252'?>
+<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
+  <?define UpdateKeyPath = 'SOFTWARE\Google\Update' ?>
+
+  <!-- This fragment assumes machine install (ALLUSER=1). -->
+
+  <Fragment Id='FragmentGoogleUpdate' >
+    <!-- Detect whether product is already registered with Google Update. -->
+    <Property Id='PRODUCT_REGISTERED_VERSION'>
+      <RegistrySearch Id='$(var.ProductNameLegalIdentifier)RegistrationSearch'
+                      Root='HKLM'
+                      Key='$(var.UpdateKeyPath)\Clients\$(var.ProductGuid)'
+                      Name='pv' Type='raw' />
+    </Property>
+
+    <Binary Id='GoogleUpdateSetup'
+            SourceFile='$(var.GoogleUpdateMetainstallerPath)' />
+
+    <!-- This fragment needs a component that can be referenced.
+         Components must be in Directory[Ref] elements and cannot be empty.
+         Thus, we have a registry value write that has no effect.
+         To avoid multiple Directory elements with the 'ProgramFilesFolder' Id,
+         assume that the product MSI has defined a it and reference it here. -->
+    <DirectoryRef Id='ProgramFilesFolder'>
+      <Directory Id='GoogleProgramDir' Name='Google'>
+        <Directory Id='UpdateDir' Name='Update'>
+
+          <Component Id='ComponentGoogleUpdate'
+                     Guid='6B528A57-0CD8-4b26-85F8-1CA05523B8F1'>
+            <RegistryValue Id='NonEmptyComponent' Action='write'
+                           Root='HKLM' Key='$(var.UpdateKeyPath)'
+                           Name='EnterpriseInstall' Type='integer' Value='1' />
+          </Component>
+
+        </Directory>
+      </Directory>
+    </DirectoryRef>
+
+    <CustomAction Id='SetProductTagProperty'
+      Property='ProductTag'
+      Value='appguid=$(var.ProductGuid)&amp;appname=$(var.ProductName)&amp;needsAdmin=True'
+      Execute='immediate'
+      Return='check' />
+    <CustomAction Id='AppendCustomParamsToProductTagProperty'
+      Property='ProductTag'
+      Value='[ProductTag]$(var.ProductCustomParams)'
+      Execute='immediate'
+      Return='check' />
+    <CustomAction Id='AppendBrandToProductTagProperty'
+      Property='ProductTag'
+      Value='[ProductTag]&amp;brand=[BRAND]'
+      Execute='immediate'
+      Return='check' />
+    <CustomAction Id='RollbackGoogleUpdateRegistration'
+      BinaryKey='GoogleUpdateSetup'
+      Impersonate='no'
+      Execute='rollback'
+      ExeCommand='/unregisterproduct "[ProductTag]"'
+      Return='check' />
+    <CustomAction Id='InstallGoogleUpdateAndRegister'
+      BinaryKey='GoogleUpdateSetup'
+      Impersonate='no'
+      Execute='deferred'
+      ExeCommand='/registerproduct "[ProductTag]"'
+      Return='check' />
+
+    <InstallExecuteSequence>
+      <!-- Build tag property. -->
+      <Custom Action='SetProductTagProperty'
+              Before='AppendCustomParamsToProductTagProperty'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL) AND (NOT DISABLE_UPDATES)
+      </Custom>
+      <Custom Action='AppendCustomParamsToProductTagProperty'
+              Before='AppendBrandToProductTagProperty'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL) AND (NOT DISABLE_UPDATES)
+      </Custom>
+      <Custom Action='AppendBrandToProductTagProperty'
+              Before='RollbackGoogleUpdateRegistration'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL) AND (NOT DISABLE_UPDATES) AND (BRAND &lt;&gt; "")
+      </Custom>
+
+      <!-- Install Google Update before 'InstallFiles' first so that brand, etc.
+           will be present when the product is installed. -->
+      <Custom Action='RollbackGoogleUpdateRegistration'
+              Before='InstallGoogleUpdateAndRegister'
+              >
+        (($ComponentGoogleUpdate>2) OR REINSTALL) AND (NOT DISABLE_UPDATES) AND (PRODUCT_REGISTERED_VERSION = "")
+      </Custom>
+      <Custom Action='InstallGoogleUpdateAndRegister'
+              Before='InstallFiles'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL) AND (NOT DISABLE_UPDATES)
+      </Custom>
+
+      <!-- Google Update will uninstall itself if the product is the only app it
+           so no need to have an uninstall operation. -->
+
+    </InstallExecuteSequence>
+
+  </Fragment>
+</Wix>
diff --git a/enterprise/public_apps.py b/enterprise/public_apps.py
new file mode 100644
index 0000000..e843e02
--- /dev/null
+++ b/enterprise/public_apps.py
@@ -0,0 +1,33 @@
+#!/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)
+EXTERNAL_APPS = [
+      ('Google Chrome',
+       '{8A69D345-D564-463C-AFF1-A69D9E530F96}',
+       ' Check http://www.google.com/chrome/.'),
+    ]
diff --git a/enterprise/test_gold.adm b/enterprise/test_gold.adm
new file mode 100644
index 0000000..172179f
--- /dev/null
+++ b/enterprise/test_gold.adm
@@ -0,0 +1,197 @@
+CLASS MACHINE

+  CATEGORY !!Cat_Google

+    CATEGORY !!Cat_GoogleUpdate

+      KEYNAME !!Key_BaseName

+      EXPLAIN !!Explain_GoogleUpdate

+

+      CATEGORY !!Cat_Preferences

+        KEYNAME !!Key_BaseName

+        EXPLAIN !!Explain_Preferences

+

+        POLICY !!Pol_AutoUpdateCheckPeriod

+          EXPLAIN !!Explain_AutoUpdateCheckPeriod

+          PART !!Part_AutoUpdateCheckPeriod NUMERIC

+            VALUENAME AutoUpdateCheckPeriodMinutes

+            DEFAULT 1400  ; 23 hours 20 minutes.

+            MIN 30

+            MAX 65535     ; 45.5 days.

+            SPIN 60       ; Increment in hour chunks.

+          END PART

+        END POLICY

+

+      END CATEGORY  ; Preferences

+

+      CATEGORY !!Cat_Applications

+        KEYNAME !!Key_BaseName

+        EXPLAIN !!Explain_Applications

+

+        POLICY !!Pol_DefaultAllowInstallation

+          EXPLAIN !!Explain_DefaultAllowInstallation

+          VALUENAME InstallDefault

+          VALUEOFF  NUMERIC 0

+          VALUEON   NUMERIC 1

+        END POLICY

+

+        POLICY !!Pol_DefaultUpdatePolicy

+          EXPLAIN !!Explain_DefaultUpdatePolicy

+          PART !!Part_UpdatePolicy DROPDOWNLIST

+            VALUENAME UpdateDefault

+            ITEMLIST

+              NAME  !!Name_AutomaticUpdates

+              VALUE NUMERIC 1

+              NAME  !!Name_ManualUpdates

+              VALUE NUMERIC 2

+              NAME  !!Name_UpdatesDisabled

+              VALUE NUMERIC 0

+            END ITEMLIST

+            REQUIRED

+          END PART

+        END POLICY

+

+        CATEGORY !!Cat_GoogleChrome

+          KEYNAME !!Key_BaseName

+

+          POLICY !!Pol_AllowInstallation

+            EXPLAIN !!Explain_InstallGoogleChrome

+            VALUENAME Install{8A69D345-D564-463C-AFF1-A69D9E530F96}

+            VALUEOFF  NUMERIC 0

+            VALUEON   NUMERIC 1

+          END POLICY

+

+          POLICY !!Pol_UpdatePolicy

+            EXPLAIN !!Explain_AutoUpdateGoogleChrome

+            PART !!Part_UpdatePolicy DROPDOWNLIST

+              VALUENAME Update{8A69D345-D564-463C-AFF1-A69D9E530F96}

+              ITEMLIST

+                NAME  !!Name_AutomaticUpdates

+                VALUE NUMERIC 1

+                NAME  !!Name_ManualUpdates

+                VALUE NUMERIC 2

+                NAME  !!Name_UpdatesDisabled

+                VALUE NUMERIC 0

+              END ITEMLIST

+              REQUIRED

+            END PART

+          END POLICY

+

+        END CATEGORY  ; Google Chrome

+

+        CATEGORY !!Cat_GoogleEarth

+          KEYNAME !!Key_BaseName

+

+          POLICY !!Pol_AllowInstallation

+            EXPLAIN !!Explain_InstallGoogleEarth

+            VALUENAME Install{74AF07D8-FB8F-4D51-8AC7-927721D56EBB}

+            VALUEOFF  NUMERIC 0

+            VALUEON   NUMERIC 1

+          END POLICY

+

+          POLICY !!Pol_UpdatePolicy

+            EXPLAIN !!Explain_AutoUpdateGoogleEarth

+            PART !!Part_UpdatePolicy DROPDOWNLIST

+              VALUENAME Update{74AF07D8-FB8F-4D51-8AC7-927721D56EBB}

+              ITEMLIST

+                NAME  !!Name_AutomaticUpdates

+                VALUE NUMERIC 1

+                NAME  !!Name_ManualUpdates

+                VALUE NUMERIC 2

+                NAME  !!Name_UpdatesDisabled

+                VALUE NUMERIC 0

+              END ITEMLIST

+              REQUIRED

+            END PART

+          END POLICY

+

+        END CATEGORY  ; Google Earth

+

+      END CATEGORY  ; Applications

+

+    END CATEGORY  ; GoogleUpdate

+

+  END CATEGORY  ; Google

+

+;------------------------------------------------------------------------------

+

+[strings]

+Key_BaseName=Software\Policies\Google\Update

+

+Cat_Google=Google

+Cat_GoogleUpdate=Google Update

+Cat_Preferences=Preferences

+Cat_Applications=Applications

+

+Pol_AutoUpdateCheckPeriod=Auto-update check period override

+Pol_DefaultAllowInstallation=Allow installation default

+Pol_AllowInstallation=Allow installation

+Pol_DefaultUpdatePolicy=Update policy override default

+Pol_UpdatePolicy=Update policy override

+

+Part_AutoUpdateCheckPeriod=Minutes between update checks

+Part_UpdatePolicy=Policy

+

+Name_AutomaticUpdates=Automatic silent updates (recommended)

+Name_ManualUpdates=Manual updates only

+Name_UpdatesDisabled=Updates disabled

+

+Cat_GoogleChrome=Google Chrome

+Cat_GoogleEarth=Google Earth

+

+Explain_GoogleUpdate=Policies to control the installation and updating of Google applications that use Google Update/Google Installer.

+

+;------------------------------------------------------------------------------

+; Preferences

+;------------------------------------------------------------------------------

+

+Explain_Preferences=General policies for Google Update.

+

+Explain_AutoUpdateCheckPeriod=Minimum number of minutes between automatic update checks.

+

+;------------------------------------------------------------------------------

+; Applications

+;------------------------------------------------------------------------------

+

+Explain_Applications=Policies for individual applications.\

+    \n\nAn updated ADM template will be required to support Google applications released in the future.

+

+Explain_DefaultAllowInstallation=Specifies the default behavior for whether Google software can be installed using Google Update/Google Installer.\

+    \n\nCan be overridden by the "Allow installation" for individual applications.\

+    \n\nOnly affects installation of Google software using Google Update/Google Installer. Cannot prevent running the application installer directly or installation of Google software that does not use Google Update/Google Installer for installation.

+

+Explain_DefaultUpdatePolicy=Specifies the default policy for software updates from Google.\

+    \n\nCan be overridden by the "Update policy override" for individual applications.\

+    \n\nOptions:\

+    \n - Automatic silent updates: Updates are automatically applied when they are found via the periodic update check.\

+    \n - Manual updates only: Updates are only applied when the user does a manual update check. (Not all apps provide an interface for this.)\

+    \n - Updates disabled: Never apply updates.\

+    \n\nIf you select manual updates, you should periodically check for updates using each application's manual update mechanism if available. If you disable updates, you should periodically check for updates and distribute them to users.\

+    \n\nOnly affects updates for Google software that uses Google Update for updates. Does not prevent auto-updates of Google software that does not use Google Update for updates.\

+    \n\nUpdates for Google Update are not affected by this setting; Google Update will continue to update itself while it is installed.\

+    \n\nWARNING: Disabing updates will also prevent updates of any new Google applications released in the future, possibly including dependencies for future versions of installed applications.

+

+;------------------------------------------------------------------------------

+; Individual Applications

+;------------------------------------------------------------------------------

+

+; Google Chrome

+Explain_InstallGoogleChrome=Specifies whether Google Chrome can be installed using Google Update/Google Installer.\

+    \n\nIf this policy is not configured, Google Chrome can be installed as specified by "Allow installation default".

+

+Explain_AutoUpdateGoogleChrome=Specifies how Google Update handles available Google Chrome updates from Google.\

+    \n\nIf this policy is not configured, Google Update handles available updates as specified by "Update policy override default".\

+    \n\nOptions:\

+    \n - Automatic silent updates: Updates are automatically applied when they are found via the periodic update check.\

+    \n - Manual updates only: Updates are only applied when the user does a manual update check. (Not all apps provide an interface for this.)\

+    \n - Updates disabled: Never apply updates.\

+    \n\nIf you select manual updates, you should periodically check for updates using the application's manual update mechanism if available. If you disable updates, you should periodically check for updates and distribute them to users. Check http://www.google.com/chrome/.

+

+; Google Earth

+Explain_InstallGoogleEarth=Specifies whether Google Earth can be installed using Google Update/Google Installer.\

+    \n\nIf this policy is not configured, Google Earth can be installed as specified by "Allow installation default".

+

+Explain_AutoUpdateGoogleEarth=Specifies how Google Update handles available Google Earth updates from Google.\

+    \n\nIf this policy is not configured, Google Update handles available updates as specified by "Update policy override default".\

+    \n\nOptions:\

+    \n - Automatic silent updates: Updates are automatically applied when they are found via the periodic update check.\

+    \n - Manual updates only: Updates are only applied when the user does a manual update check. (Not all apps provide an interface for this.)\

+    \n - Updates disabled: Never apply updates.\

+    \n\nIf you select manual updates, you should periodically check for updates using the application's manual update mechanism if available. If you disable updates, you should periodically check for updates and distribute them to users. Check http://earth.google.com/.

diff --git a/google_update/GoogleUpdate.manifest b/google_update/GoogleUpdate.manifest
new file mode 100644
index 0000000..1cf9856
--- /dev/null
+++ b/google_update/GoogleUpdate.manifest
@@ -0,0 +1,22 @@
+<?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>
+  <dependency>
+    <dependentAssembly>
+        <assemblyIdentity
+            type="win32"
+            name="Microsoft.Windows.Common-Controls"
+            version="6.0.0.0"
+            processorArchitecture="X86"
+            publicKeyToken="6595b64144ccf1df"
+            language="*"
+        />
+    </dependentAssembly>
+  </dependency>
+</assembly>
diff --git a/google_update/bin/Readme.txt b/google_update/bin/Readme.txt
new file mode 100644
index 0000000..b30b79f
--- /dev/null
+++ b/google_update/bin/Readme.txt
@@ -0,0 +1,9 @@
+This directory contains the saved "constant" version of the GoogleUpdate.exe
+shell. Using the same executable avoids firewall prompts in some cases when
+updating Google Update.
+
+This shell can only be used with goopdate.dll files that have been signed with
+the real certificate.
+
+When updating the shell, also change the expected version in
+..\google_update_unittest.cc.
diff --git a/google_update/bin/dbg/GoogleUpdate.exe b/google_update/bin/dbg/GoogleUpdate.exe
new file mode 100644
index 0000000..8ceeb50
--- /dev/null
+++ b/google_update/bin/dbg/GoogleUpdate.exe
Binary files differ
diff --git a/google_update/bin/dbg/GoogleUpdate_unsigned_exe.pdb b/google_update/bin/dbg/GoogleUpdate_unsigned_exe.pdb
new file mode 100644
index 0000000..a96a764
--- /dev/null
+++ b/google_update/bin/dbg/GoogleUpdate_unsigned_exe.pdb
Binary files differ
diff --git a/google_update/bin/opt/GoogleUpdate.exe b/google_update/bin/opt/GoogleUpdate.exe
new file mode 100644
index 0000000..3edfbc9
--- /dev/null
+++ b/google_update/bin/opt/GoogleUpdate.exe
Binary files differ
diff --git a/google_update/bin/opt/GoogleUpdate_unsigned_exe.pdb b/google_update/bin/opt/GoogleUpdate_unsigned_exe.pdb
new file mode 100644
index 0000000..8baed31
--- /dev/null
+++ b/google_update/bin/opt/GoogleUpdate_unsigned_exe.pdb
Binary files differ
diff --git a/google_update/build.scons b/google_update/build.scons
new file mode 100644
index 0000000..eb1179e
--- /dev/null
+++ b/google_update/build.scons
@@ -0,0 +1,152 @@
+#!/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.
+# ========================================================================
+
+# We always build the shell as GoogleUpdate_built.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.
+#
+# Changes to this executable will not appear in offical builds until they are
+# included in an offical build and the resulting file is checked in to the
+# saved constant shell location.
+
+
+Import('env')
+
+# Only build the first version. We don't need a test version.
+v = env['product_version'][0]
+version_string = '%d.%d.%d.%d' % (v[0], v[1], v[2], v[3])
+
+# The shell contains languages not supported by the rest of Omaha.
+# This is intended to allow us to add languages in the future without releasing
+# a new shell.
+# 'userdefault' addresses apps that don't look up the resource for the OS
+# language. See http://b/1328652.
+
+shell_languages = list(env['languages'])
+
+additional_shell_languages = [
+    'userdefault',
+    'zh-HK',
+    ]
+
+if 1 < len(shell_languages):
+  shell_languages += additional_shell_languages
+
+
+exe_env = env.Clone()
+
+exe_env.Append(
+    CCFLAGS = [
+        '/wd4548',
+        '/wd4917',
+        '/wd4265',
+        '/FIgoogle_update/precompile.h',
+        ],
+    LIBS = [
+        'delayimp.lib',
+        'advapi32.lib',
+        'crypt32.lib',
+        'kernel32.lib',
+        'ole32.lib',
+        'shell32.lib',
+        'shlwapi.lib',
+        'user32.lib',
+        'wintrust.lib',
+        ('atls.lib', 'atlsd.lib')[exe_env.Bit('debug')],
+        ('libcmt.lib', 'libcmtd.lib')[exe_env.Bit('debug')],
+        ('libcpmt.lib', 'libcpmtd.lib')[exe_env.Bit('debug')],
+        ],
+    LINKFLAGS = [
+        '/NODEFAULTLIB',
+        '/MERGE:.rdata=.text'
+        '/DELAYLOAD:advapi32.dll',
+        '/DELAYLOAD:crypt32.dll',
+        '/DELAYLOAD:shell32.dll',
+        '/DELAYLOAD:shlwapi.dll',
+        '/DELAYLOAD:user32.dll',
+        '/DELAYLOAD:wintrust.dll',
+
+        # Forces the dependency on ole32.lib.
+        '/INCLUDE:_CoCreateGuid@4',
+        ],
+    RCFLAGS = [
+        '/DVERSION_MAJOR=%d' % v[0],
+        '/DVERSION_MINOR=%d' % v[1],
+        '/DVERSION_BUILD=%d' % v[2],
+        '/DVERSION_PATCH=%d' % v[3],
+        '/DVERSION_NUMBER_STRING=\\"%s\\"' % version_string,
+        ],
+)
+
+
+exe_inputs = [
+    'winmain.cc',
+     '../common/signaturevalidator.cc',
+     exe_env.RES('resource.rc'),
+     ]
+
+# Compile .rc files, then add the resulting .res files to the exe inputs.
+for language in shell_languages:
+  exe_inputs += exe_env.RES('generated_resources_%s.rc' % language)
+
+# Force a rebuild when the version changes. The en file should be enough to
+# rebuild all languages.
+Depends(exe_env['OBJ_ROOT'] + '/google_update/generated_resources_en.res',
+    exe_env['MAIN_DIR'] + '/VERSION')
+
+# Need to add custom suffix to avoid target conflict with
+# common/signaturevalidator.obj
+exe_env['OBJSUFFIX'] = '_gu' + exe_env['OBJSUFFIX']
+
+
+# We disable runtime stack check to avoid increasing the code size.
+# There is a compiler pragma to programmatically disable the stack checks
+# but for some reason it did not work.
+exe_env.FilterOut(CPPFLAGS = ['/GS'])
+
+# Disable stack checks for VC80. Stack checks are on by default.
+if exe_env['msc_ver'] >= 1400:
+  exe_env['CCFLAGS'] += ['/GS-']
+
+unsigned_exe_output = exe_env.ComponentProgram(
+    prog_name='GoogleUpdate_unsigned.exe',
+    source=exe_inputs,
+)
+
+sign_output = env.SignedBinary(
+    target='GoogleUpdate_signed.exe',
+    source=unsigned_exe_output,
+)
+
+env.Replicate('$STAGING_DIR', sign_output)
+
+# Official builds should always use the checked in constant shell unless they
+# are being built with the test certificate, in which case the saved constant
+# shell would fail to validate the certificates in the build.
+if env.Bit('build_server') and not env.Bit('test_certificate'):
+  # Copy the constant shell from its checked in location.
+  source_shell = ('$MAIN_DIR/google_update/bin/%s/GoogleUpdate.exe' %
+      ('opt', 'dbg')[env.Bit('debug')])
+else:
+  # Use the version we just built.
+  source_shell = sign_output[0]
+
+env.Replicate('$STAGING_DIR', source_shell, REPLICATE_REPLACE=[('_signed', '')])
+env.Replicate(target='$STAGING_DIR',
+              source=sign_output[0],
+              REPLICATE_REPLACE=[('GoogleUpdate_signed', 'GoogleCrashHandler')]
+)
diff --git a/google_update/generated_resources_ar.rc b/google_update/generated_resources_ar.rc
new file mode 100644
index 0000000..54fee44
--- /dev/null
+++ 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
new file mode 100644
index 0000000..7952225
--- /dev/null
+++ 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
new file mode 100644
index 0000000..8d318fc
--- /dev/null
+++ 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
new file mode 100644
index 0000000..5d9ab59
--- /dev/null
+++ 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
new file mode 100644
index 0000000..1542f1f
--- /dev/null
+++ 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
new file mode 100644
index 0000000..597639b
--- /dev/null
+++ 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
new file mode 100644
index 0000000..c8b69d2
--- /dev/null
+++ 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
new file mode 100644
index 0000000..65dba7f
--- /dev/null
+++ 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
new file mode 100644
index 0000000..2b21a6c
--- /dev/null
+++ 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
new file mode 100644
index 0000000..caad7e7
--- /dev/null
+++ 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
new file mode 100644
index 0000000..b7b7f08
--- /dev/null
+++ 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
new file mode 100644
index 0000000..acb0763
--- /dev/null
+++ 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
new file mode 100644
index 0000000..a4f4528
--- /dev/null
+++ 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
new file mode 100644
index 0000000..24e414a
--- /dev/null
+++ 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
new file mode 100644
index 0000000..9568222
--- /dev/null
+++ 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
new file mode 100644
index 0000000..ffb9803
--- /dev/null
+++ 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
new file mode 100644
index 0000000..bb82ec3
--- /dev/null
+++ 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
new file mode 100644
index 0000000..40e5a0e
--- /dev/null
+++ 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
new file mode 100644
index 0000000..2bc8ea9
--- /dev/null
+++ 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
new file mode 100644
index 0000000..9e2fa8a
--- /dev/null
+++ 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
new file mode 100644
index 0000000..3a90e0d
--- /dev/null
+++ 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
new file mode 100644
index 0000000..f5f455b
--- /dev/null
+++ 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
new file mode 100644
index 0000000..ec8e504
--- /dev/null
+++ 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
new file mode 100644
index 0000000..dca20dc
--- /dev/null
+++ 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
new file mode 100644
index 0000000..d3ed1a2
--- /dev/null
+++ 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
new file mode 100644
index 0000000..a57bb19
--- /dev/null
+++ 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
new file mode 100644
index 0000000..02537ac
--- /dev/null
+++ 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
new file mode 100644
index 0000000..b674691
--- /dev/null
+++ 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
new file mode 100644
index 0000000..ff05b43
--- /dev/null
+++ 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
new file mode 100644
index 0000000..18f5d6e
--- /dev/null
+++ 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
new file mode 100644
index 0000000..a5e3ccc
--- /dev/null
+++ 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
new file mode 100644
index 0000000..36edc9b
--- /dev/null
+++ 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
new file mode 100644
index 0000000..a11225f
--- /dev/null
+++ 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
new file mode 100644
index 0000000..ab86977
--- /dev/null
+++ 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
new file mode 100644
index 0000000..9f5c769
--- /dev/null
+++ 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
new file mode 100644
index 0000000..b0c9107
--- /dev/null
+++ 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
new file mode 100644
index 0000000..17388ac
--- /dev/null
+++ 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
new file mode 100644
index 0000000..f0567e8
--- /dev/null
+++ 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
new file mode 100644
index 0000000..657aa08
--- /dev/null
+++ 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
new file mode 100644
index 0000000..2b35bd4
--- /dev/null
+++ 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
new file mode 100644
index 0000000..3603639
--- /dev/null
+++ 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
new file mode 100644
index 0000000..19a2ddb
--- /dev/null
+++ 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
new file mode 100644
index 0000000..4c35a72
--- /dev/null
+++ 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
new file mode 100644
index 0000000..f2b6d5f
--- /dev/null
+++ 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
new file mode 100644
index 0000000..282b4bc
--- /dev/null
+++ b/google_update/generated_resources_sv.rc
Binary files differ
diff --git a/google_update/generated_resources_ta.rc b/google_update/generated_resources_ta.rc
new file mode 100644
index 0000000..46b0d31
--- /dev/null
+++ 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
new file mode 100644
index 0000000..571939f
--- /dev/null
+++ 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
new file mode 100644
index 0000000..40fee4d
--- /dev/null
+++ 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
new file mode 100644
index 0000000..ab7e33d
--- /dev/null
+++ 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
new file mode 100644
index 0000000..9a796fa
--- /dev/null
+++ 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
new file mode 100644
index 0000000..2ebce6b
--- /dev/null
+++ 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
new file mode 100644
index 0000000..71ac396
--- /dev/null
+++ 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
new file mode 100644
index 0000000..8e746bf
--- /dev/null
+++ 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
new file mode 100644
index 0000000..b9c56d4
--- /dev/null
+++ 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
new file mode 100644
index 0000000..cbbcf56
--- /dev/null
+++ 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
new file mode 100644
index 0000000..dc5e2e6
--- /dev/null
+++ 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
new file mode 100644
index 0000000..6623270
--- /dev/null
+++ b/google_update/google_update.grh
@@ -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.
+// ========================================================================
+// This file is automatically generated by GRIT.  Do not edit.
+// Built on Mon Apr 06 11:33:31 2009
+
+#ifndef RESOURCE_734604429686__
+#define RESOURCE_734604429686__
+
+
+
+#endif // RESOURCE_734604429686__
diff --git a/google_update/google_update_unittest.cc b/google_update/google_update_unittest.cc
new file mode 100644
index 0000000..d01f60f
--- /dev/null
+++ b/google_update/google_update_unittest.cc
@@ -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.
+// ========================================================================
+
+#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/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(File::Exists(actual_shell_path));
+  const ULONGLONG actual_shell_version =
+      app_util::GetVersionFromFile(actual_shell_path);
+
+  EXPECT_TRUE(actual_shell_version);
+#if TEST_CERTIFICATE
+  EXPECT_EQ(OMAHA_BUILD_VERSION, actual_shell_version);
+  EXPECT_STREQ(OMAHA_BUILD_VERSION_STRING,
+               StringFromVersion(actual_shell_version));
+#else
+  // This version must be updated whenever the saved shell is updated.
+  EXPECT_STREQ(_T("1.2.131.7"), StringFromVersion(actual_shell_version));
+#endif
+}
+
+}  // namespace omaha
diff --git a/google_update/precompile.h b/google_update/precompile.h
new file mode 100644
index 0000000..92f5693
--- /dev/null
+++ b/google_update/precompile.h
@@ -0,0 +1,37 @@
+// 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_GOOGLE_UPDATE_PRECOMPILE_H__
+#define OMAHA_GOOGLE_UPDATE_PRECOMPILE_H__
+
+#pragma runtime_checks("", off)
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+#include <shlwapi.h>
+#include <tchar.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)
+
+#endif  // OMAHA_GOOGLE_UPDATE_PRECOMPILE_H__
diff --git a/google_update/resource.h b/google_update/resource.h
new file mode 100644
index 0000000..d2e8802
--- /dev/null
+++ b/google_update/resource.h
@@ -0,0 +1,22 @@
+// 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_GOOGLE_UPDATE_RESOURCE_H_
+#define OMAHA_GOOGLE_UPDATE_RESOURCE_H_
+
+// Icons.
+#define IDI_APP                         101
+
+#endif // OMAHA_GOOGLE_UPDATE_RESOURCE_H_
diff --git a/google_update/resource.rc b/google_update/resource.rc
new file mode 100644
index 0000000..5a35419
--- /dev/null
+++ b/google_update/resource.rc
@@ -0,0 +1,27 @@
+// 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 "resource.h"
+#include "afxres.h"
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+1 RT_MANIFEST "GoogleUpdate.manifest"
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP                 ICON                    "omaha/goopdate/goopdate.ico"
+
diff --git a/google_update/version.rc b/google_update/version.rc
new file mode 100644
index 0000000..c74e5ba
--- /dev/null
+++ b/google_update/version.rc
@@ -0,0 +1,57 @@
+// 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 <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-2009 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
new file mode 100644
index 0000000..1d36f39
--- /dev/null
+++ b/google_update/winmain.cc
@@ -0,0 +1,298 @@
+// 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 small shell that loads a DLL calls its well known entry point.
+// The intention is to only depend on OS mechanisms and avoid the LIBC
+// dependency completely.
+// This indirection is done primarily to:
+// Play nicely with software firewalls. Most firewalls watch the executable
+// module making network requests and we do not want them to notify the user
+// after we've updated the program. The DLL can change independently of the
+// shell and we expect the shell to remain unchanged most of the time.
+//
+// Changes to this executable will not appear in offical builds until they are
+// included in an offical build and the resulting file is checked in to the
+// saved shell location.
+
+// Disable the RTC checks because this shell doesn't build with a CRT.
+#pragma runtime_checks("", off)
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <tchar.h>
+
+#include <atlbase.h>
+#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/goopdate/main.h"
+
+namespace omaha {
+
+// Disable the stack checks to keep the code size down.
+// The check_stack pragma did not really work. The stack checks had to be
+// disabled in the mk_file.
+#pragma check_stack()
+
+// Have to define this here since this is used in signaturevalidator.cpp.
+// This is defined in error.cc, but that pulls in debug.cpp which has a lot
+// of additional dependencies we don't want.  Not worth it for just this
+// function.
+HRESULT HRESULTFromLastError() {
+  DWORD error_code = ::GetLastError();
+  return (error_code != NO_ERROR) ? HRESULT_FROM_WIN32(error_code) : E_FAIL;
+}
+
+// Adapted from File::Exists in file.cpp.
+bool FileExists(const TCHAR* file_name) {
+  if (!file_name || !*file_name) {
+    return false;
+  }
+
+  WIN32_FILE_ATTRIBUTE_DATA attrs = {0};
+  return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs);
+}
+
+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);
+    known = true;
+  }
+  return is_vista;
+}
+
+bool IsRunningFromProgramFilesDirectory() {
+  // Get the HMODULE for the current process.
+  HMODULE module_handle = ::GetModuleHandle(NULL);
+  if (!module_handle) {
+    return false;
+  }
+
+  // Get the full path to the module based on the HMODULE.
+  CString module_path;
+  DWORD result = ::GetModuleFileName(module_handle,
+                                     module_path.GetBufferSetLength(MAX_PATH),
+                                     MAX_PATH);
+  module_handle = NULL;
+
+  if (result == 0) {
+    return false;
+  }
+
+  // Get the directory of the current process without the filename.
+  CPath path_temp(module_path);
+  path_temp.RemoveFileSpec();
+  module_path = static_cast<CString>(path_temp);
+
+  // Get the directory to %ProgramFiles%.
+  TCHAR folder_path_buffer[MAX_PATH] = {0};
+  HRESULT hr = ::SHGetFolderPath(NULL,
+                                 CSIDL_PROGRAM_FILES,
+                                 NULL,
+                                 SHGFP_TYPE_CURRENT,
+                                 folder_path_buffer);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  // Append the google/update install path onto %ProgramFiles%.
+  CString folder_path = folder_path_buffer;
+  if (!::PathAppend(CStrBuf(folder_path, MAX_PATH),
+                    OMAHA_REL_GOOPDATE_INSTALL_DIR)) {
+    return false;
+  }
+
+  folder_path.MakeLower();
+  module_path.MakeLower();
+
+  // Check if module_path starts with folder_path.
+  return (module_path.Find(folder_path) == 0);
+}
+
+// If we're not running from program files and we're running as an elevated
+// user on Vista, we need to validate the signature of goopdate.dll before
+// loading it.
+HRESULT VerifySignatureIfNecessary(const TCHAR* file_path,
+                                   bool is_running_from_program_files) {
+  if (is_running_from_program_files ||
+      !IsVistaOrLater() ||
+      !::IsUserAnAdmin()) {
+    return S_OK;
+  }
+
+  // Verify the Authenticode signature but use only the local cache for
+  // revocation checks.
+  HRESULT hr = VerifySignature(file_path, false);
+#if TEST_CERTIFICATE
+  // The chain of trust will not validate on builds signed with the test
+  // certificate.
+  if (CERT_E_UNTRUSTEDROOT == hr) {
+    hr = S_OK;
+  }
+#endif
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Verify that there is a Google certificate and that it has not expired.
+  if (!VerifySigneeIsGoogle(file_path)) {
+    return GOOGLEUPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED;
+  }
+
+  return S_OK;
+}
+
+HRESULT GetRegisteredVersion(bool is_machine, CString* version) {
+  HKEY key = NULL;
+  LONG res = ::RegOpenKeyEx(is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+                            GOOPDATE_REG_RELATIVE_CLIENTS GOOPDATE_APP_ID,
+                            0,
+                            KEY_READ,
+                            &key);
+  if (ERROR_SUCCESS != res) {
+    return HRESULT_FROM_WIN32(res);
+  }
+
+  DWORD type = 0;
+  DWORD version_length = 50;
+  res = ::SHQueryValueEx(key,
+                         omaha::kRegValueProductVersion,
+                         NULL,
+                         &type,
+                         CStrBuf(*version, version_length),
+                         &version_length);
+  if (ERROR_SUCCESS != res) {
+    return HRESULT_FROM_WIN32(res);
+  }
+
+  if (REG_SZ != type) {
+    return E_UNEXPECTED;
+  }
+
+  return S_OK;
+}
+
+HRESULT GetDllPath(HINSTANCE instance, bool is_machine, CString* dll_path) {
+  TCHAR base_path[MAX_PATH] = {0};
+  TCHAR path[MAX_PATH] = {0};
+
+  if (!::GetModuleFileName(instance, base_path, arraysize(base_path))) {
+    return HRESULTFromLastError();
+  }
+  ::PathRemoveFileSpec(base_path);
+
+  // Try the side-by-side DLL first.
+  _tcscpy_s(path, arraysize(path), base_path);
+  if (!::PathAppend(path, omaha::kGoopdateDllName)) {
+    return HRESULTFromLastError();
+  }
+  if (FileExists(path)) {
+    *dll_path = path;
+    return S_OK;
+  }
+
+  // Try the version subdirectory.
+  _tcscpy_s(path, arraysize(path), base_path);
+  CString version;
+  HRESULT hr = GetRegisteredVersion(is_machine, &version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  if (!::PathAppend(path, version)) {
+    return HRESULTFromLastError();
+  }
+  if (!::PathAppend(path, omaha::kGoopdateDllName)) {
+    return HRESULTFromLastError();
+  }
+  if (!FileExists(path)) {
+    return GOOGLEUPDATE_E_DLL_NOT_FOUND;
+  }
+
+  *dll_path = path;
+  return S_OK;
+}
+
+}  // namespace omaha
+
+// Algorithm:
+//  * Looks for goopdate.dll in the current directory.
+//  * If it is not found, looks for goopdate.dll in a version subdirectory based
+//    on the version found in the registry.
+//  * Verifies the signature of the goopdate.dll file.
+//  * Loads the DLL and calls the entry point.
+int WINAPI _tWinMain(HINSTANCE instance,
+                     HINSTANCE,
+                     LPTSTR,
+                     int cmd_show) {
+  bool is_running_from_program_files =
+      omaha::IsRunningFromProgramFilesDirectory();
+
+  // We assume here that running from program files means we should check
+  // the machine install version and otherwise we should check the user
+  // version. This should be true in all end user cases except for initial
+  // installs from the temp directory and OneClick cross-installs. In both
+  // cases, the DLL should be in the same directory so we should not get here.
+  // For developer use cases, the DLL should also be in the same directory.
+  bool is_machine = is_running_from_program_files;
+
+  CString dll_path;
+  HRESULT hr = omaha::GetDllPath(instance, is_machine, &dll_path);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = omaha::VerifySignatureIfNecessary(dll_path,
+                                         is_running_from_program_files);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  HMODULE module(::LoadLibraryEx(dll_path, NULL, 0));
+  if (!module) {
+    return omaha::HRESULTFromLastError();
+  }
+
+  DllEntry dll_entry = reinterpret_cast<DllEntry>(
+      ::GetProcAddress(module, omaha::kGoopdateDllEntryAnsi));
+  if (dll_entry) {
+    // We must send in GetCommandLine() and not cmd_line because the command
+    // line parsing code expects to have the program name as the first argument
+    // and cmd_line does not provide this.
+    hr = dll_entry(::GetCommandLine(), cmd_show);
+  } else {
+    hr = E_FAIL;
+  }
+
+  ::FreeLibrary(module);
+
+  return hr;
+}
+
diff --git a/goopdate/browser_launcher.cc b/goopdate/browser_launcher.cc
new file mode 100644
index 0000000..720b54f
--- /dev/null
+++ b/goopdate/browser_launcher.cc
@@ -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.
+// ========================================================================
+
+
+#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 core.
+// When starting, 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);
+
+  SharedMemoryAttributes attr(kGoogleUpdateCoreSharedMemoryName,
+                              CSecurityDesc());
+  GoogleUpdateCoreProxy google_update_core_proxy(true, &attr);
+  CComPtr<IGoogleUpdateCore> google_update_core;
+  HRESULT hr = google_update_core_proxy.GetObject(&google_update_core);
+  if (FAILED(hr)) {
+    CORE_LOG(L3, (_T("[GetObject for IGoogleUpdateCore failed][0x%08x]"), hr));
+
+    hr = google_update_core.CoCreateInstance(__uuidof(GoogleUpdateCoreClass));
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[CoCreate GoogleUpdateCoreClass failed][0x%08x]"), 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
new file mode 100644
index 0000000..ae1bce6
--- /dev/null
+++ b/goopdate/browser_launcher.h
@@ -0,0 +1,101 @@
+// 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
new file mode 100644
index 0000000..4b2d537
--- /dev/null
+++ b/goopdate/build.scons
@@ -0,0 +1,238 @@
+#!/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 Goopdate library
+#
+midl_env = env.Clone()
+midl_env.Tool('midl')
+midl_env['MIDLFLAGS'] += [
+    '/Oicf',  # generate optimized stubless proxy/stub code
+    ]
+
+# Compile the .idl file into .c & .h files
+midl_env.TypeLibrary('google_update_idl.idl')
+
+
+gd_env = env.Clone()
+
+# Need to look in output dir to find .h files generated by midl compiler.
+gd_env['CPPPATH'] += [
+    '$OBJ_ROOT',
+    '$MAIN_DIR/third_party/breakpad/src/',
+    ]
+
+target_name = 'goopdate_dll.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',
+    'crash.cc',
+    'extra_args_parser.cc',
+    'event_logger.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',
+    'resource_manager.cc',
+    'ui_displayed_event.cc',
+    'webplugin_utils.cc',
+    ]
+if env.Bit('use_precompiled_headers'):
+  gd_inputs += gd_env.EnablePrecompile(target_name)
+
+# Compile the library.
+gd_env.ComponentLibrary(target_name, gd_inputs)
+
+
+#
+# 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')
+
+
+# Make sure that if there are any languages at all, then
+# default and hong kong chinese are added (why?)
+translated_languages = list(env['languages'])
+if 1 < len(translated_languages):
+  translated_languages += [
+      'userdefault',
+      'zh-HK',
+      ]
+
+#
+# Build Goopdate DLL
+#
+_first = True
+for v in env['product_version']:
+  temp_env = env.Clone(COMPONENT_STATIC=False)
+
+  if _first:
+    _first = False
+    prefix = ''
+  else:
+    prefix = 'TEST_'
+    temp_env['OBJPREFIX'] = temp_env.subst('test/$OBJPREFIX')
+
+  version_string = '%d.%d.%d.%d' % (v[0], v[1], v[2], v[3])
+
+  temp_env.Append(
+      CPPPATH = [
+          '$MAIN_DIR/third_party/breakpad/src/',
+          ],
+
+      # 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.
+      LIBS = [
+          '$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/google_update_recovery.lib',
+          '$LIB_DIR/logging.lib',
+          '$LIB_DIR/net.lib',
+          '$LIB_DIR/repair_goopdate.lib',
+          '$LIB_DIR/security.lib',
+          '$LIB_DIR/service.lib',
+          '$LIB_DIR/setup.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',
+          'delayimp.lib',
+          'iphlpapi.lib',
+          'netapi32.lib',
+          'msi.lib',
+          'mstask.lib',
+          'psapi.lib',
+          'rasapi32.lib',
+          'rpcns4.lib',
+          'rpcrt4.lib',
+          'shlwapi.lib',
+          'version.lib',
+          'urlmon.lib',
+          'userenv.lib',
+          'wininet.lib',
+          'wintrust.lib',
+          'ws2_32.lib',
+          '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.
+          '/INCLUDE:_WSAStartup@8',
+
+          # TODO(Omaha) - Choose a rebase address which does not conflict
+          # with other DLLs loaded in our process. For now, we just picked
+          # an arbitrary address.
+          '/BASE:0x18000000',
+          ],
+      RCFLAGS = [
+          '/DVERSION_MAJOR=%d' % v[0],
+          '/DVERSION_MINOR=%d' % v[1],
+          '/DVERSION_BUILD=%d' % v[2],
+          '/DVERSION_PATCH=%d' % v[3],
+          '/DVERSION_NUMBER_STRING=\\"%s\\"' % version_string,
+
+          # goopdate.dll is resource neutral.
+          '/DLANGAUGE_STRING=\\"en\\"',
+          ],
+  )
+
+  version_res = temp_env.RES(
+      target=prefix + 'goopdate_version.res',
+      source='goopdate_version.rc'
+  )
+
+  # Force a rebuild when the version changes.
+  env.Depends(version_res, '$MAIN_DIR/VERSION')
+
+  target_name = prefix + 'goopdate_unsigned.dll'
+
+  # 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
+  # description of the problem and thus a different solution, but this worked.
+  inputs = [
+      'goopdate.def',
+      'main.cc',
+      temp_env.RES(prefix + 'goopdate.res', 'goopdate.rc'),
+      version_res,
+      ]
+  if env.Bit('use_precompiled_headers'):
+    inputs += temp_env.EnablePrecompile(target_name)
+
+  for language in translated_languages:
+    lang_base_name = 'goopdate_dll/generated_resources_' + language
+    inputs += temp_env.RES(
+        target='resources/%s.res' % (prefix + lang_base_name),
+        source='resources/%s.rc' % lang_base_name,
+    )
+
+  unsigned_dll = temp_env.ComponentLibrary(
+      lib_name=target_name,
+      source=inputs,
+  )
+
+  signed_dll = temp_env.SignedBinary(
+      target=prefix + 'goopdate.dll',
+      source=unsigned_dll,
+  )
+
+  env.Replicate('$STAGING_DIR', signed_dll)
+  env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb'])
+
+
+# Build all the resource dlls.
+env.BuildSConscript('resources')
diff --git a/goopdate/command_line.cc b/goopdate/command_line.cc
new file mode 100644
index 0000000..29650f5
--- /dev/null
+++ b/goopdate/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/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
new file mode 100644
index 0000000..d410553
--- /dev/null
+++ b/goopdate/command_line.h
@@ -0,0 +1,139 @@
+// 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
new file mode 100644
index 0000000..39f51e1
--- /dev/null
+++ b/goopdate/command_line_builder.cc
@@ -0,0 +1,499 @@
+// 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);
+  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 {
+  CString cmd_line;
+  if (is_uninstall_set_) {
+    cmd_line.Format(_T("/%s /%s"), kCmdLineUpdateApps, kCmdLineUninstall);
+  } else {
+    cmd_line = GetSingleSwitch(kCmdLineUpdateApps);
+  }
+  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
new file mode 100644
index 0000000..569da7d
--- /dev/null
+++ b/goopdate/command_line_builder.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.
+// ========================================================================
+
+#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
new file mode 100644
index 0000000..3e0c3b1
--- /dev/null
+++ b/goopdate/command_line_builder_unittest.cc
@@ -0,0 +1,505 @@
+// 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);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/ua"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildUAWithUninstall) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UA);
+  builder.set_is_uninstall_set(true);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/ua /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
new file mode 100644
index 0000000..5bdc9a6
--- /dev/null
+++ b/goopdate/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_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
new file mode 100644
index 0000000..66e9d3f
--- /dev/null
+++ b/goopdate/command_line_parser.cc
@@ -0,0 +1,377 @@
+// 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
new file mode 100644
index 0000000..196d5af
--- /dev/null
+++ b/goopdate/command_line_parser.h
@@ -0,0 +1,109 @@
+// 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
new file mode 100644
index 0000000..be730e9
--- /dev/null
+++ b/goopdate/command_line_parser_unittest.cc
@@ -0,0 +1,161 @@
+// 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
new file mode 100644
index 0000000..2697eab
--- /dev/null
+++ b/goopdate/command_line_unittest.cc
@@ -0,0 +1,1290 @@
+// 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_Ua) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ua");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UA;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /ua /uninstall
+TEST_F(CommandLineTest, ParseCommandLine_UaWithUninstall) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ua /uninstall");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UA;
+  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> /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
new file mode 100644
index 0000000..4e11746
--- /dev/null
+++ b/goopdate/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/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
new file mode 100644
index 0000000..027a865
--- /dev/null
+++ b/goopdate/command_line_validator.h
@@ -0,0 +1,108 @@
+// 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
new file mode 100644
index 0000000..60ab516
--- /dev/null
+++ b/goopdate/command_line_validator_unittest.cc
@@ -0,0 +1,130 @@
+// 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
new file mode 100644
index 0000000..b83ac73
--- /dev/null
+++ b/goopdate/config_manager.cc
@@ -0,0 +1,831 @@
+// 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/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::GetDownloadStorage() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
+                           CString(OMAHA_REL_DOWNLOAD_STORAGE_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;
+}
+
+HRESULT ConfigManager::GetWebPluginCheckUrl(CString* url) const {
+  ASSERT1(url);
+
+#ifdef DEBUG
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNameWebPluginUrl,
+                                 url))) {
+    CORE_LOG(L5, (_T("['webplugin url' override %s]"), *url));
+    return S_OK;
+  }
+#endif
+  *url = kUrlWebPluginCheck;
+  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.
+int ConfigManager::GetLastCheckPeriodSec(bool* is_overridden) const {
+  ASSERT1(is_overridden);
+  DWORD registry_period_sec = 0;
+  *is_overridden = GetLastCheckPeriodSecFromRegistry(&registry_period_sec);
+  if (*is_overridden) {
+    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);
+}
+
+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("[ConfigManager::GetCurrentTaskNameCore]")));
+
+  CString default_name(goopdate_utils::GetDefaultGoopdateTaskName(is_machine));
+  default_name += kScheduledTaskNameCoreSuffix;
+  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));
+  default_name += kScheduledTaskNameCoreSuffix;
+  return CreateAndSetVersionedNameInRegistry(is_machine,
+                                             default_name,
+                                             kRegValueTaskNameC);
+}
+
+CString ConfigManager::GetCurrentTaskNameUA(bool is_machine) {
+  CORE_LOG(L3, (_T("[ConfigManager::GetCurrentTaskNameUA]")));
+
+  CString default_name(goopdate_utils::GetDefaultGoopdateTaskName(is_machine));
+  default_name += kScheduledTaskNameUASuffix;
+  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));
+  default_name += kScheduledTaskNameUASuffix;
+  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 in audit mode and OEM install time is present. Requiring the
+// OEM install time prevents ever blocking non-OEM installs from updating.
+// Also returns true if less than kMinOemModeMs since the OEM install.
+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;
+  }
+
+  if (IsWindowsInstalling()) {
+    CORE_LOG(L3, (_T("[IsOemInstalling][IsWindowsInstalling][true]")));
+    return true;
+  }
+
+  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 uint32 kMinOemModeSeconds = kMinOemModeMs / kMsPerSec;
+  const bool result = time_difference_seconds < kMinOemModeSeconds ? 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
new file mode 100644
index 0000000..de69344
--- /dev/null
+++ b/goopdate/config_manager.h
@@ -0,0 +1,260 @@
+// 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();
+  }
+
+  // Creates download data dir:
+  // %UserProfile%/Application Data/Google/Update/Downloads
+  CString GetDownloadStorage() 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 service endpoint where the webplugin parameters
+  // are being sent for validation.
+  HRESULT GetWebPluginCheckUrl(CString* url) const;
+
+  // Returns the time interval between update checks in seconds.
+  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 "GoogleUpdateTaskMachine1c9b3d6baf90df3Core", 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 "GoogleUpdateTaskMachine1c9b3d6baf90df3UA", 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);
+
+  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
new file mode 100644
index 0000000..a60c6c7
--- /dev/null
+++ b/goopdate/config_manager_unittest.cc
@@ -0,0 +1,1804 @@
+// 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 <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);
+
+  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));
+}
+
+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) {
+  DWORD val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueLastCheckPeriodSec,
+                                    val));
+  bool is_overridden = false;
+  EXPECT_EQ(60, 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(60, 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 = 0;
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kOverrideMinutes));
+  bool is_overridden = false;
+  EXPECT_EQ(60, 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(cm_->CanOverInstall(), !OFFICIAL_BUILD);
+#endif
+
+  val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNameOverInstall,
+                                    val));
+#ifdef DEBUG
+  EXPECT_FALSE(cm_->CanOverInstall());
+#else
+  EXPECT_EQ(cm_->CanOverInstall(), !OFFICIAL_BUILD);
+#endif
+}
+
+// Tests AuCheckPeriodMs override.
+TEST_F(ConfigManagerTest, GetAutoUpdateTimerIntervalMs) {
+  EXPECT_EQ(cm_->GetAutoUpdateTimerIntervalMs(), kAUCheckPeriodGooglerMs);
+
+  DWORD val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetAutoUpdateTimerIntervalMs(), kMinAUCheckPeriodMs);
+
+  val = kMinAUCheckPeriodMs - 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetAutoUpdateTimerIntervalMs(), kMinAUCheckPeriodMs);
+
+  val = 30000;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetAutoUpdateTimerIntervalMs(), val);
+
+  val = INT_MAX;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetAutoUpdateTimerIntervalMs(), val);
+
+  // Tests overflow with large positive numbers.
+  val = INT_MAX;
+  ++val;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetAutoUpdateTimerIntervalMs(), INT_MAX);
+
+  val = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetAutoUpdateTimerIntervalMs(), INT_MAX);
+}
+
+// Tests CrCheckPeriodMs override.
+TEST_F(ConfigManagerTest, GetCodeRedTimerIntervalMs) {
+  EXPECT_EQ(cm_->GetCodeRedTimerIntervalMs(), kCodeRedCheckPeriodMs);
+
+  DWORD val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetCodeRedTimerIntervalMs(), kMinCodeRedCheckPeriodMs);
+
+  val = kMinCodeRedCheckPeriodMs - 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetCodeRedTimerIntervalMs(), kMinCodeRedCheckPeriodMs);
+
+  val = 60000;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetCodeRedTimerIntervalMs(), val);
+
+  val = INT_MAX;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetCodeRedTimerIntervalMs(), val);
+
+  // Tests overflow with large positive numbers.
+  val = INT_MAX;
+  ++val;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(cm_->GetCodeRedTimerIntervalMs(), INT_MAX);
+
+  val = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                      kRegValueCrCheckPeriodMs,
+                                      val));
+  EXPECT_EQ(cm_->GetCodeRedTimerIntervalMs(), INT_MAX);
+}
+
+// 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_TRUE(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_TRUE(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_TRUE(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));
+}
+
+TEST_F(ConfigManagerTest, IsGoogler) {
+  EXPECT_TRUE(cm_->IsGoogler());
+}
+
+TEST_F(ConfigManagerTest, IsWindowsInstalling_Normal) {
+  EXPECT_FALSE(cm_->IsWindowsInstalling());
+}
+
+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());
+}
+
+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_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);
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/const_goopdate.h b/goopdate/const_goopdate.h
new file mode 100644
index 0000000..eec7422
--- /dev/null
+++ b/goopdate/const_goopdate.h
@@ -0,0 +1,161 @@
+// 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.
+// ========================================================================
+//
+// 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");
+
+// 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 kRegValueMachineId             = _T("mi");
+const TCHAR* const kRegValueOemInstallTimeSec     = _T("OemInstallTime");
+const TCHAR* const kRegValueInstalledPath         = _T("path");
+const TCHAR* const kRegValueUserId                = _T("ui");
+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 kScheduledTaskNameMachine    = _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;