diff options
author | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
---|---|---|
committer | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
commit | 05b47f7a8c5451f858dc220df0e3a97542edace6 (patch) | |
tree | a2273d619f0625c9d44d40842845ccce2eac1045 /o3d/google_update | |
parent | 5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff) | |
download | chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2 |
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't
yet build in it's new home. We'll change that shortly.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/google_update')
-rw-r--r-- | o3d/google_update/build.scons | 43 | ||||
-rw-r--r-- | o3d/google_update/google_update_idl.idl | 243 | ||||
-rw-r--r-- | o3d/google_update/performondemand.cc | 546 | ||||
-rw-r--r-- | o3d/google_update/performondemand.h | 193 |
4 files changed, 1025 insertions, 0 deletions
diff --git a/o3d/google_update/build.scons b/o3d/google_update/build.scons new file mode 100644 index 0000000..1573a14 --- /dev/null +++ b/o3d/google_update/build.scons @@ -0,0 +1,43 @@ +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Import('env') + +idl_stuff = env.TypeLibrary('google_update_idl.idl') +env.Append(CPPPATH=env.Dir('.')) + +INPUTS = [ + 'performondemand.cc', +] + +exe = env.ComponentProgram('getextras', INPUTS) + +env.Requires(exe, idl_stuff) + +env.Replicate('$ARTIFACTS_DIR', exe) diff --git a/o3d/google_update/google_update_idl.idl b/o3d/google_update/google_update_idl.idl new file mode 100644 index 0000000..4b8f499 --- /dev/null +++ b/o3d/google_update/google_update_idl.idl @@ -0,0 +1,243 @@ +// Copyright 2007-2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== +// +// The proxy clsid #defined as PROXY_CLSID_IS in mk_common needs to be changed +// anytime any interface below changes, or if a new interface is added. + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(5B25A8DC-1780-4178-A629-6BE8B8DEFAA2), + oleautomation, + nonextensible, + pointer_default(unique) +] +interface IBrowserHttpRequest2 : IUnknown { + // This method will send request/data from the browser process. + // @param url URL where request will be send. + // @param post_data POST data, if any. Can be NULL. + // @param request_headers HTTP request headers, if any. Can be NULL. + // @param response_headers_needed HTTP response headers that are needed. + // Should be one of the values listed here: + // http://go/queryinfoflags + // The input is a SAFEARRAY of DWORD. Can be a + // VT_EMPTY. + // @param response_headers HTTP response headers, returned as SAFEARRAY + // of BSTR. The values corresponding one-to-one + // with the response_headers_needed values. Can + // be NULL if response_headers_needed==VT_EMPTY + // @param response_code HTTP response code. + // @param cache_filename Cache file that contains the response data. + HRESULT Send([in] BSTR url, + [in] BSTR post_data, + [in] BSTR request_headers, + [in] VARIANT response_headers_needed, + [out] VARIANT* response_headers, + [out] DWORD* response_code, + [out] BSTR* cache_filename); +}; + +[ + object, + oleautomation, + uuid(128C2DA6-2BC0-44c0-B3F6-4EC22E647964), + helpstring("Google Update IProcessLauncher Interface"), + pointer_default(unique) +] +interface IProcessLauncher : IUnknown { + // @param cmd_line The full command line to execute. + HRESULT LaunchCmdLine([in, string] const WCHAR* cmd_line); + + // @param browser_type The browser to start. + // @param url The url to launch the browser with. + HRESULT LaunchBrowser([in] DWORD browser_type, + [in, string] const WCHAR* url); + + // @param app_id Unique id to identify the calling client application + // @param event_id Unique id for the command + // @param caller_proc_id The process id of the calling process + // @param proc_handle The process handle valid in the caller's context + HRESULT LaunchCmdElevated([in, string] const WCHAR* app_guid, + [in, string] const WCHAR* cmd_id, + [in] DWORD caller_proc_id, + [out] ULONG_PTR* proc_handle); +}; + +typedef enum { + COMPLETION_CODE_SUCCESS = 1, + COMPLETION_CODE_SUCCESS_CLOSE_UI, + COMPLETION_CODE_ERROR, + COMPLETION_CODE_RESTART_ALL_BROWSERS, + COMPLETION_CODE_REBOOT, + COMPLETION_CODE_RESTART_BROWSER, + COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY, + COMPLETION_CODE_REBOOT_NOTICE_ONLY, + COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY, + COMPLETION_CODE_RUN_COMMAND, +} CompletionCodes; + +[ + object, + oleautomation, + uuid(1C642CED-CA3B-4013-A9DF-CA6CE5FF6503), + helpstring("GoogleUpdate UI-specific events Interface"), + pointer_default(unique) +] +interface IProgressWndEvents : IUnknown { + // The UI is closing down. The user has clicked on either the "X" or the + // other buttons of the UI to close the window. + HRESULT DoClose(); + + // Pause has been clicked on. + HRESULT DoPause(); + + // Resume has been clicked on. + HRESULT DoResume(); + + // RestartBrowsers button has been clicked on. + HRESULT DoRestartBrowsers(); + + // Reboot button has been clicked on. + HRESULT DoReboot(); + + // Launch Browser. + HRESULT DoLaunchBrowser([in, string] const WCHAR* url); +}; + + +[ + object, + oleautomation, + uuid(49D7563B-2DDB-4831-88C8-768A53833837), + helpstring("IJobObserver Interface"), + pointer_default(unique) +] +interface IJobObserver : IUnknown { + HRESULT OnShow(); + HRESULT OnCheckingForUpdate(); + HRESULT OnUpdateAvailable([in, string] const WCHAR* version_string); + HRESULT OnWaitingToDownload(); + HRESULT OnDownloading([in] int time_remaining_ms, [in] int pos); + HRESULT OnWaitingToInstall(); + HRESULT OnInstalling(); + HRESULT OnPause(); + HRESULT OnComplete([in] CompletionCodes code, + [in, string] const WCHAR* reserved); + HRESULT SetEventSink([in] IProgressWndEvents* ui_sink); +}; + +// TODO: Component story. +// TODO: Should CheckForUpdate be synchronous? We can avoid the +// observer. However, it may block the UI. +// TODO: On-Demand Installs. +[ + object, + oleautomation, + uuid(31AC3F11-E5EA-4a85-8A3D-8E095A39C27B), + helpstring("IGoogleUpdate Interface"), + pointer_default(unique) +] +interface IGoogleUpdate : IUnknown { + // @param guid The guid for the app to be updated. + // @param observer The eventing interface. + HRESULT CheckForUpdate([in, string] const WCHAR* guid, + [in] IJobObserver* observer); + + // @param guid The guid for the app to be updated. + // @param observer The eventing interface. + HRESULT Update([in, string] const WCHAR* guid, + [in] IJobObserver* observer); +}; + +// IGoogleUpdateCore is an internal Omaha interface. +[ + object, + oleautomation, + uuid(909489C2-85A6-4322-AA56-D25278649D67), + helpstring("Google Update Core Interface"), + pointer_default(unique) +] +interface IGoogleUpdateCore : IUnknown +{ + // Runs a command elevated. + // + // @param app_id Unique id to identify the calling client application + // @param event_id Unique id for the command + // @param caller_proc_id The process id of the calling process + // @param proc_handle The process handle valid in the caller's context + HRESULT LaunchCmdElevated([in, string] const WCHAR* app_guid, + [in, string] const WCHAR* cmd_id, + [in] DWORD caller_proc_id, + [out] ULONG_PTR* proc_handle); +}; + +[ + uuid(7E6CD20B-8688-4960-96D9-B979471577B8), + version(1.0), + helpstring("Google Update Type Library") +] +library GoogleUpdateLib { + importlib("stdole2.tlb"); + [ + uuid(ABC01078-F197-4b0b-ADBC-CFE684B39C82), + helpstring("ProcessLauncherClass Class") + ] + coclass ProcessLauncherClass { + [default] interface IProcessLauncher; + } + + // This coclass declaration exists only for the purpose of forcing + // ::RegisterTypeLib() to register the interfaces within. This is + // required so that we can marshal/unmarshal the interfaces using the TypeLib + // marshaler. + [ + uuid(9564861C-3469-4c9a-956A-74D5690790E6), + helpstring("InterfaceRegistrar Class") + ] + coclass InterfaceRegistrar { + [default] interface IBrowserHttpRequest2; + interface IJobObserver; + interface IProgressWndEvents; + } + + [ + uuid(2F0E2680-9FF5-43c0-B76E-114A56E93598), + helpstring("OnDemand updates for per-user applications.") + ] + coclass OnDemandUserAppsClass { + [default] interface IGoogleUpdate; + } + + [ + uuid(6F8BD55B-E83D-4a47-85BE-81FFA8057A69), + helpstring("OnDemand updates for per-machine applications.") + ] + coclass OnDemandMachineAppsClass { + [default] interface IGoogleUpdate; + } + + [ + uuid(E225E692-4B47-4777-9BED-4FD7FE257F0E), + helpstring("GoogleUpdateCore Class") + ] + coclass GoogleUpdateCoreClass + { + [default] interface IGoogleUpdateCore; + } + +}; + diff --git a/o3d/google_update/performondemand.cc b/o3d/google_update/performondemand.cc new file mode 100644 index 0000000..0b7977c --- /dev/null +++ b/o3d/google_update/performondemand.cc @@ -0,0 +1,546 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// A simple tool for performing and interacting with on demand updates. + +#include "google_update/performondemand.h" +#include <windows.h> +#include <sddl.h> +#include <shlobj.h> +#include <atltime.h> +#include <tchar.h> + +namespace o3d { + +namespace google_update { + +// 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()); + if (NO_ERROR != ::CLSIDFromString(s, &guid)) { + // Docs don't guarantee that the guid's unmodified on failure. + guid = GUID_NULL; + } + } + return guid; +} + +#define arraysize(x) (sizeof(x) / sizeof(x[0])) + +CString GuidToString(const GUID& guid) { + TCHAR guid_str[40] = {0}; + ::StringFromGUID2(guid, guid_str, arraysize(guid_str)); + ::CharUpperBuff(guid_str, arraysize(guid_str)); + return guid_str; +} + +HRESULT GetRegKeyValue(HKEY hkey_parent, const TCHAR *key_name, + const TCHAR * value_name, TCHAR * * value) { + HKEY hkey; + LONG res = ::RegOpenKeyEx(hkey_parent, key_name, 0, KEY_READ, &hkey); + HRESULT hr = HRESULT_FROM_WIN32(res); + + if (hr != S_OK) { + return hr; + } + + DWORD byte_count = 0; + DWORD type = 0; + + // first get the size of the string buffer + res = ::SHQueryValueEx(hkey, value_name, NULL, &type, NULL, &byte_count); + 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(hkey, value_name, NULL, &type, + reinterpret_cast<byte*>(*value), &byte_count); + hr = HRESULT_FROM_WIN32(res); + } else { + (*value)[0] = _T('\0'); + } + + if (hr == S_OK && (type != REG_SZ) && (type != REG_MULTI_SZ) && + (type != REG_EXPAND_SZ)) { + hr = E_UNEXPECTED; + } + } else { + hr = E_OUTOFMEMORY; + } + } + + ::RegCloseKey(hkey); + return hr; +} + +HRESULT SetRegKeyValue(HKEY hkey_parent, const TCHAR *key_name, + const TCHAR *value_name, const TCHAR *value, DWORD value_size) { + HKEY hkey; + LONG res = ::RegCreateKeyEx(hkey_parent, key_name, 0, 0, + REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hkey, NULL); + HRESULT hr = HRESULT_FROM_WIN32(res); + + if (hr != S_OK) { + return hr; + } + + res = ::RegSetValueEx(hkey, value_name, 0, REG_SZ, + reinterpret_cast<const BYTE *>(value), value_size); + hr = HRESULT_FROM_WIN32(res); + + ::RegCloseKey(hkey); + return hr; +} + +// Reads the Proxy information for the given interface from HKCU, and registers +// it with COM. +HRESULT RegisterHKCUPSClsid(IID iid, + HMODULE* proxy_module, + DWORD* revoke_cookie) { + *proxy_module = NULL; + *revoke_cookie = 0; + + const TCHAR* const hkcu_classes_key = _T("Software\\Classes\\"); + + // Get the registered proxy for the interface. + CString interface_proxy_clsid_key; + interface_proxy_clsid_key.Format(_T("%sInterface\\%s\\ProxyStubClsid32"), + hkcu_classes_key, GuidToString(iid)); + TCHAR *proxy_clsid32_value; + HRESULT hr = GetRegKeyValue(HKEY_CURRENT_USER, + interface_proxy_clsid_key, + NULL, + &proxy_clsid32_value); + if (FAILED(hr)) { + return hr; + } + + // Get the location of the proxy/stub DLL. + CString proxy_server32_entry; + proxy_server32_entry.Format(_T("%sClsid\\%s\\InprocServer32"), + hkcu_classes_key, proxy_clsid32_value); + TCHAR *hkcu_proxy_dll_path; + hr = GetRegKeyValue(HKEY_CURRENT_USER, + proxy_server32_entry, + NULL, + &hkcu_proxy_dll_path); + if (FAILED(hr)) { + return hr; + } + + // Get the proxy/stub class object. + typedef HRESULT (STDAPICALLTYPE *DllGetClassObjectTypedef)(REFCLSID clsid, + REFIID iid, + void** ptr); + *proxy_module = ::LoadLibrary(hkcu_proxy_dll_path); + DllGetClassObjectTypedef fn = NULL; + + fn = reinterpret_cast<DllGetClassObjectTypedef>( + ::GetProcAddress(*proxy_module, "DllGetClassObject")); + if (!fn) { + hr = HRESULT_FROM_WIN32(::GetLastError()); + return hr; + } + CComPtr<IPSFactoryBuffer> fb; + CLSID proxy_clsid = StringToGuid(proxy_clsid32_value); + hr = (*fn)(proxy_clsid, IID_IPSFactoryBuffer, reinterpret_cast<void**>(&fb)); + if (FAILED(hr)) { + return hr; + } + + // Register the proxy/stub class object. + hr = ::CoRegisterClassObject(proxy_clsid, fb, CLSCTX_INPROC_SERVER, + REGCLS_MULTIPLEUSE, revoke_cookie); + if (FAILED(hr)) { + return hr; + } + + // Relate the interface with the proxy/stub, so COM does not do a lookup when + // unmarshaling the interface. + hr = ::CoRegisterPSClsid(iid, proxy_clsid); + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} + +// Assumes you're running on Vista; call IsRunningOnVista first to check. +bool IsUserRunningSplitToken() { + HANDLE process_token; + if (!::OpenProcessToken(GetCurrentProcess(), + TOKEN_QUERY, + &process_token)) { + return false; + } + + TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault; + DWORD size_returned = 0; + bool ret = false; + if (::GetTokenInformation(process_token, + TokenElevationType, + &elevation_type, + sizeof(elevation_type), + &size_returned)) { + ret = elevation_type == TokenElevationTypeFull || + elevation_type == TokenElevationTypeLimited; + } + ::CloseHandle(process_token); + + return ret; +} + +// If this function fails to find any of the info it's looking for, it'll +// default to saying "No, this isn't Vista". +bool IsRunningOnVista() { + // Use GetVersionEx to get OS and Service Pack information. + OSVERSIONINFOEX osviex; + ::ZeroMemory(&osviex, sizeof(OSVERSIONINFOEX)); + osviex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + BOOL success = ::GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&osviex)); + + // If ::GetVersionEx fails when given an OSVERSIONINFOEX then we're running + // on NT4.0SP5 or earlier. + if (!success) { + return false; + } else if (osviex.dwPlatformId == VER_PLATFORM_WIN32_NT && + osviex.dwMajorVersion == 6 && osviex.dwMinorVersion == 0) { + return true; + } + + return false; +} + +// Sets the thread token to medium integrity, which allows for out-of-proc HKCU +// COM server activation. For more info on process integrity levels, see +// http://en.wikipedia.org/wiki/Mandatory_Integrity_Control. +DWORD SetTokenIntegrityLevelMedium(HANDLE token) { + PSID medium_sid = NULL; + if (!::ConvertStringSidToSid(SDDL_ML_MEDIUM, &medium_sid)) { + return ::GetLastError(); + } + + TOKEN_MANDATORY_LABEL label = {0}; + label.Label.Attributes = SE_GROUP_INTEGRITY; + label.Label.Sid = medium_sid; + + size_t size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid); + BOOL success = ::SetTokenInformation(token, TokenIntegrityLevel, &label, + size); + DWORD result = success ? ERROR_SUCCESS : ::GetLastError(); + ::LocalFree(medium_sid); + return result; +} + +// A helper class for clients of the Google Update on-demand out-of-proc COM +// server. An instance of this class is typically created on the stack. The +// class does nothing for cases where the OS is not Vista with UAC off. +// This class does the following: +// * Calls CoInitializeSecurity with cloaking set to dynamic. This makes COM +// use the thread token instead of the process token. +// * Impersonates and sets the thread token to medium integrity. This allows for +// out-of-proc HKCU COM server activation. +// * Reads and registers per-user proxies for the interfaces that on-demand +// exposes. +class VistaProxyRegistrar { + public: + VistaProxyRegistrar() + : googleupdate_cookie_(0), + jobobserver_cookie_(0), + progresswndevents_cookie_(0), + is_impersonated(false), + failed(false), + googleupdate_library_(NULL), + jobobserver_library_(NULL), + progresswndevents_library_(NULL) { + HRESULT hr = VistaProxyRegistrarImpl(); + if (FAILED(hr)) { + failed = true; + } + } + + ~VistaProxyRegistrar() { + if (googleupdate_cookie_) { + if (FAILED((::CoRevokeClassObject(googleupdate_cookie_)))) { + // TODO: How can we get errors out of the installer? + // LOG(ERROR("CoRevokeClassObject failed.")); + } + } + + if (jobobserver_cookie_) { + if (FAILED((::CoRevokeClassObject(jobobserver_cookie_)))) { + // LOG(ERROR("CoRevokeClassObject failed.")); + } + } + + if (progresswndevents_cookie_) { + if (FAILED((::CoRevokeClassObject(progresswndevents_cookie_)))) { + // LOG(ERROR("CoRevokeClassObject failed.")); + } + } + + if (is_impersonated) { + if (FAILED(::RevertToSelf())) { + // LOG(ERROR("RevertToSelf failed.")); + } + } + ::FreeLibrary(googleupdate_library_); + ::FreeLibrary(jobobserver_library_); + ::FreeLibrary(progresswndevents_library_); + } + + private: + HRESULT VistaProxyRegistrarImpl() { + if (!IsRunningOnVista() || + IsUserRunningSplitToken() || + !::IsUserAnAdmin()) { + return S_OK; + } + + // Needs to be called very early on in a process. + // Turn on dynamic cloaking so COM picks up the impersonated thread token. + HRESULT hr = ::CoInitializeSecurity( + NULL, + -1, + NULL, + NULL, + RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IDENTIFY, + NULL, + EOAC_DYNAMIC_CLOAKING, + NULL); + if (FAILED(hr)) { + return hr; + } + + is_impersonated = !!::ImpersonateSelf(SecurityImpersonation); + if (!is_impersonated) { + hr = HRESULT_FROM_WIN32(::GetLastError()); + return hr; + } + + HANDLE thread_token; + if (!::OpenThreadToken(::GetCurrentThread(), + TOKEN_ALL_ACCESS, + false, + &thread_token)) { + hr = HRESULT_FROM_WIN32(::GetLastError()); + return hr; + } + + DWORD result = SetTokenIntegrityLevelMedium(thread_token); + ::CloseHandle(thread_token); + if (result != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(result); + } + + hr = RegisterHKCUPSClsid(__uuidof(IGoogleUpdate), + &googleupdate_library_, + &googleupdate_cookie_); + if (FAILED(hr)) { + return hr; + } + + hr = RegisterHKCUPSClsid(__uuidof(IJobObserver), + &jobobserver_library_, + &jobobserver_cookie_); + if (FAILED(hr)) { + return hr; + } + + hr = RegisterHKCUPSClsid(__uuidof(IProgressWndEvents), + &progresswndevents_library_, + &progresswndevents_cookie_); + if (FAILED(hr)) { + return hr; + } + + return S_OK; + } + + private: + HMODULE googleupdate_library_; + HMODULE jobobserver_library_; + HMODULE progresswndevents_library_; + + DWORD googleupdate_cookie_; + DWORD jobobserver_cookie_; + DWORD progresswndevents_cookie_; + bool is_impersonated; + bool failed; +}; + +class scoped_co_init { + public: + scoped_co_init() { + hr_ = ::CoInitializeEx(0, COINIT_APARTMENTTHREADED); + } + ~scoped_co_init() { + if (SUCCEEDED(hr_)) + ::CoUninitialize(); + } + private: + HRESULT hr_; +}; + +int PerformOnDemandInstall(CString guid) { + // Verify that the guid is valid. + GUID parsed = StringToGuid(guid); + if (parsed == GUID_NULL) { + return -1; + } + + // Set a fake registry value that tells Google Update that the package is + // already installed, but with an ancient version that needs updating. This + // is because Google Update doesn't really support install-on-demand, only + // update-on-demand. + // TODO: Check with the google_update team if this does any damage if the + // software's already installed. If so, we should attempt to determine + // whether it's installed first--although we should be wary of trusting + // these registry entries, which might be stale. + CString key_path(_T("Software\\Google\\Update\\Clients\\")); + key_path += guid; + CString value = _T("0.0.0.1"); + HRESULT hr = SetRegKeyValue(HKEY_CURRENT_USER, key_path, _T("pv"), + value, value.GetLength() * sizeof(TCHAR)); + + if (FAILED(hr)) { + return hr; + } + int timeout = 60; + CComModule module; + scoped_co_init com_apt; + VistaProxyRegistrar registrar; + + CComObject<JobObserver>* job_observer; + hr = CComObject<JobObserver>::CreateInstance(&job_observer); + if (!SUCCEEDED(hr)) { + return -1; + } + CComPtr<IJobObserver> job_holder(job_observer); + + CComPtr<IGoogleUpdate> on_demand; + hr = on_demand.CoCreateInstance(__uuidof(OnDemandUserAppsClass)); + + if (!SUCCEEDED(hr)) { + return -1; + } + + hr = on_demand->Update(guid, job_observer); + + if (!SUCCEEDED(hr)) { + return -1; + } + + // Main message loop: + MSG msg; + SYSTEMTIME start_system_time = {0}; + SYSTEMTIME current_system_time = {0}; + ::GetSystemTime(&start_system_time); + CTime start_time(start_system_time); + CTimeSpan timeout_period(0, 0, 0, timeout); + + while (::GetMessage(&msg, NULL, 0, 0)) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + ::GetSystemTime(¤t_system_time); + CTime current_time(current_system_time); + CTimeSpan elapsed_time = current_time - start_time; + if (timeout_period < elapsed_time) { + // TODO: Right now the timeout does correctly break, but then + // the COM interactions continue on to completion. + break; + } + } + int ret_val = job_observer->observed; + + if (!ret_val) { + return -1; // This really shouldn't happen, but just in case... + } + + if (ret_val & (JobObserver::ON_COMPLETE_SUCCESS | + JobObserver::ON_COMPLETE_SUCCESS_CLOSE_UI)) { + return 0; // The success case. + } + + return ret_val; // Otherwise tell what happened. Never sets all bits (-1). +} + +int GetD3DX9() { + return PerformOnDemandInstall("{34B2805D-C72C-4f81-AED5-5A22D1E092F1}"); +} + +} // namespace google_update + +} // namespace o3d + +// Arguments expected: +// [0]: Our binary's name, as usual +// [1]: The process ID of the Google Update process, in hex; we'll wait for it +// to exit before doing our stuff. If there's no process ID supplied, don't +// wait. +int _tmain(int argc, TCHAR* argv[]) { + if (argc > 2) { + return -1; + } + if (argc == 2) { + TCHAR *end = NULL; + DWORD google_update_id = _tcstol(argv[1], &end, 16); + if (!google_update_id || end == argv[1]) { + return -1; + } + + HANDLE google_update_handle = OpenProcess(SYNCHRONIZE, false, + google_update_id); + + if (google_update_handle) { + // 120 minutes is safer than INFINITY, but effectively the same thing. + DWORD timeout = 120 * 60 * 1000; + DWORD waitResponse = WaitForSingleObject(google_update_handle, timeout); + + if (waitResponse == WAIT_TIMEOUT || waitResponse == WAIT_FAILED) { + return -1; + } + if (waitResponse != WAIT_ABANDONED && waitResponse != WAIT_OBJECT_0) { + return -1; + } + } // Else optimistically assume it already exited. + } + return o3d::google_update::GetD3DX9(); +} diff --git a/o3d/google_update/performondemand.h b/o3d/google_update/performondemand.h new file mode 100644 index 0000000..3441940 --- /dev/null +++ b/o3d/google_update/performondemand.h @@ -0,0 +1,193 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef O3D_GOOGLE_UPDATE_PERFORMONDEMAND_H_ +#define O3D_GOOGLE_UPDATE_PERFORMONDEMAND_H_ + +#pragma once +#include <windows.h> +#include <atlbase.h> +#include <atlcom.h> +#include "google_update_idl.h" // NOLINT + +class JobObserver + : public CComObjectRootEx<CComSingleThreadModel>, + public IJobObserver { + public: + + enum ReturnCodes { + ON_COMPLETE_SUCCESS = 0x00000001, + ON_COMPLETE_SUCCESS_CLOSE_UI = 0x00000002, + ON_COMPLETE_ERROR = 0x00000004, + ON_COMPLETE_RESTART_ALL_BROWSERS = 0x00000008, + ON_COMPLETE_REBOOT = 0x00000010, + ON_SHOW = 0x00000020, + ON_CHECKING_FOR_UPDATES = 0x00000040, + ON_UPDATE_AVAILABLE = 0x00000080, + ON_WAITING_TO_DOWNLOAD = 0x00000100, + ON_DOWNLOADING = 0x00000200, + ON_WAITING_TO_INSTALL = 0x00000400, + ON_INSTALLING = 0x00000800, + ON_PAUSE = 0x00001000, + SET_EVENT_SINK = 0x00002000, + ON_COMPLETE_RESTART_BROWSER = 0x00004000, + ON_COMPLETE_RESTART_ALL_BROWSERS_NOTICE_ONLY = 0x00008000, + ON_COMPLETE_REBOOT_NOTICE_ONLY = 0x00010000, + ON_COMPLETE_RESTART_BROWSER_NOTICE_ONLY = 0x00020000, + ON_COMPLETE_RUN_COMMAND = 0x00040000, + }; + + BEGIN_COM_MAP(JobObserver) + COM_INTERFACE_ENTRY(IJobObserver) + END_COM_MAP() + + // Each interaction enables a bit in observed, which is eventually returned as + // a return code. + int observed; + + // Similar to observed, misbehave_modes_ and close_modes_ take on bits from + // the list of all events. For example, if close_modes_ | ON_DOWNLOADING + // is true, then when ON_DOWNLOADING is called, DoClose will be called. + int misbehave_modes_; + int close_modes_; + bool do_closed_called; + + JobObserver() + : observed(0), misbehave_modes_(0), close_modes_(0), + do_closed_called(false) { + } + virtual ~JobObserver() { + } + + void Reset() { + observed = 0; + misbehave_modes_ = 0; + close_modes_ = 0; + do_closed_called = false; + } + + void AddMisbehaveMode(int event_code) { + misbehave_modes_ |= event_code; + } + + void AddCloseMode(int event_code) { + close_modes_ |= event_code; + } + + HRESULT HandleEvent(int event_code) { + observed |= event_code; + + if ((event_code & close_modes_) && !do_closed_called) { + do_closed_called = true; + event_sink_->DoClose(); + } + + if (event_code & misbehave_modes_) { + return E_FAIL; + } else { + return S_OK; + } + } + + // JobObserver implementation. + STDMETHOD(OnShow)() { + return HandleEvent(ON_SHOW); + } + STDMETHOD(OnCheckingForUpdate)() { + return HandleEvent(ON_CHECKING_FOR_UPDATES); + } + STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) { + return HandleEvent(ON_UPDATE_AVAILABLE); + } + STDMETHOD(OnWaitingToDownload)() { + return HandleEvent(ON_WAITING_TO_INSTALL); + } + STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) { + return HandleEvent(ON_DOWNLOADING); + } + STDMETHOD(OnWaitingToInstall)() { + return HandleEvent(ON_WAITING_TO_INSTALL); + } + STDMETHOD(OnInstalling)() { + return HandleEvent(ON_INSTALLING); + } + STDMETHOD(OnPause)() { + return HandleEvent(ON_PAUSE); + } + STDMETHOD(OnComplete)(CompletionCodes code, const TCHAR* text) { + int event_code = 0; + switch (code) { + case COMPLETION_CODE_SUCCESS: + event_code |= ON_COMPLETE_SUCCESS; + break; + case COMPLETION_CODE_SUCCESS_CLOSE_UI: + event_code |= ON_COMPLETE_SUCCESS_CLOSE_UI; + break; + case COMPLETION_CODE_ERROR: + event_code |= ON_COMPLETE_ERROR; + break; + case COMPLETION_CODE_RESTART_ALL_BROWSERS: + event_code |= ON_COMPLETE_RESTART_ALL_BROWSERS; + break; + case COMPLETION_CODE_REBOOT: + event_code |= ON_COMPLETE_REBOOT; + break; + case COMPLETION_CODE_RESTART_BROWSER: + event_code |= ON_COMPLETE_RESTART_BROWSER; + break; + case COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY: + event_code |= ON_COMPLETE_RESTART_ALL_BROWSERS_NOTICE_ONLY; + break; + case COMPLETION_CODE_REBOOT_NOTICE_ONLY: + event_code |= ON_COMPLETE_REBOOT_NOTICE_ONLY; + break; + case COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY: + event_code |= ON_COMPLETE_RESTART_BROWSER_NOTICE_ONLY; + break; + case COMPLETION_CODE_RUN_COMMAND: + event_code |= ON_COMPLETE_RUN_COMMAND; + break; + default: + break; + } + ::PostThreadMessage(::GetCurrentThreadId(), WM_QUIT, 0, 0); + return HandleEvent(event_code); + } + STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) { + event_sink_ = event_sink; + return HandleEvent(SET_EVENT_SINK); + } + + CComPtr<IProgressWndEvents> event_sink_; +}; + +#endif // O3D_GOOGLE_UPDATE_PERFORMONDEMAND_H_ |