blob: df10df51ecca3397e7ccc24e540a02d8e3f7f57a [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "base/update_checker.h"
#if defined(OS_WIN)
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <atlbase.h>
#include <atlcom.h>
#include <string>
#include "base/scoped_handle.h"
#include "base/win_util.h"
#endif // OS_WIN
using namespace std;
namespace mozc {
namespace {
#if defined(OS_WIN) && defined(GOOGLE_JAPANESE_INPUT_BUILD)
// This GUID is specific to Google Japanese Input.
// Do not reuse this GUID for OSS Mozc.
const wchar_t kOmahaGUID[] = L"{DDCCD2A9-025E-4142-BCEB-F467B88CF830}";
// Based on "google_update_idl.idl"
// http://code.google.com/p/omaha/source/browse/trunk/goopdate/google_update_idl.idl
enum CompletionCodes {
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,
};
// "GoogleUpdate UI-specific events Interface"
// ------
// Based on "google_update_idl.idl"
// http://code.google.com/p/omaha/source/browse/trunk/goopdate/google_update_idl.idl
MIDL_INTERFACE("1C642CED-CA3B-4013-A9DF-CA6CE5FF6503")
IProgressWndEvents : public IUnknown {
public:
// The UI is closing down. The user has clicked on either the "X" or the
// other buttons of the UI to close the window.
virtual HRESULT STDMETHODCALLTYPE DoClose() = 0;
// Pause has been clicked on.
virtual HRESULT STDMETHODCALLTYPE DoPause() = 0;
// Resume has been clicked on.
virtual HRESULT STDMETHODCALLTYPE DoResume() = 0;
// RestartBrowsers button has been clicked on.
virtual HRESULT STDMETHODCALLTYPE DoRestartBrowsers() = 0;
// Reboot button has been clicked on.
virtual HRESULT STDMETHODCALLTYPE DoReboot() = 0;
// Launch Browser.
virtual HRESULT STDMETHODCALLTYPE DoLaunchBrowser(
/* [string][in] */ const WCHAR *url) = 0;
};
// Based on "google_update_idl.idl"
// http://code.google.com/p/omaha/source/browse/trunk/goopdate/google_update_idl.idl
MIDL_INTERFACE("49D7563B-2DDB-4831-88C8-768A53833837")
IJobObserver : public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE OnShow() = 0;
virtual HRESULT STDMETHODCALLTYPE OnCheckingForUpdate() = 0;
virtual HRESULT STDMETHODCALLTYPE OnUpdateAvailable(
const WCHAR *version_string) = 0; // [string][in]
virtual HRESULT STDMETHODCALLTYPE OnWaitingToDownload() = 0;
virtual HRESULT STDMETHODCALLTYPE OnDownloading(
int time_remaining_ms, // [in]
int pos) = 0; // [in]
virtual HRESULT STDMETHODCALLTYPE OnWaitingToInstall() = 0;
virtual HRESULT STDMETHODCALLTYPE OnInstalling() = 0;
virtual HRESULT STDMETHODCALLTYPE OnPause() = 0;
virtual HRESULT STDMETHODCALLTYPE OnComplete(
CompletionCodes code, // [in]
const WCHAR *reserved) = 0; // [string][in]
virtual HRESULT STDMETHODCALLTYPE SetEventSink(
IProgressWndEvents *ui_sink) = 0; // [in]
};
// Based on "google_update_idl.idl"
// http://code.google.com/p/omaha/source/browse/trunk/goopdate/google_update_idl.idl
MIDL_INTERFACE("31AC3F11-E5EA-4a85-8A3D-8E095A39C27B")
IGoogleUpdate : public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE CheckForUpdate(
const WCHAR *guid, // [string][in]
IJobObserver *observer) = 0; // [in]
virtual HRESULT STDMETHODCALLTYPE Update(
const WCHAR *guid, // [string][in]
IJobObserver *observer) = 0; // [in]
};
// OnDemand updates for per-user applications.
// ----
// Based on "google_update_idl.idl"
// http://code.google.com/p/omaha/source/browse/trunk/goopdate/google_update_idl.idl
// {6F8BD55B-E83D-4a47-85BE-81FFA8057A69};
const CLSID CLSID_OnDemandMachineAppsClass =
{0x6F8BD55B, 0xE83D, 0x4a47,
{0x85, 0xBE, 0x81, 0xFF, 0xA8, 0x05, 0x7A, 0x69}
};
class UpdateCheckJob
: public IJobObserver {
public:
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid, void **ppvObject) {
if (::IsEqualGUID(riid, _ATL_IIDOF(IJobObserver))) {
*ppvObject = static_cast<IJobObserver *>(this);
this->AddRef();
return S_OK;
}
if (::IsEqualGUID(riid, IID_IUnknown)) {
*ppvObject = static_cast<IUnknown *>(this);
this->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef() {
return ::InterlockedIncrement(&reference_count_);
}
virtual ULONG STDMETHODCALLTYPE Release() {
const ULONG count = ::InterlockedDecrement(&reference_count_);
if (count == 0) {
delete this;
}
return count;
}
STDMETHOD(OnShow)() {
return S_OK;
}
STDMETHOD(OnCheckingForUpdate)() {
upgrade_check_started_ = true;
return S_OK;
}
STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) {
new_version_available_ = true;
return S_OK;
}
STDMETHOD(OnWaitingToDownload)() {
return S_OK;
}
STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) {
return S_OK;
}
STDMETHOD(OnWaitingToInstall)() {
return S_OK;
}
STDMETHOD(OnInstalling)() {
return S_OK;
}
STDMETHOD(OnPause)() {
return S_OK;
}
STDMETHOD(OnComplete)(CompletionCodes code, const TCHAR* text) {
progress_events_ = NULL;
UpdateChecker::CallbackWParam wParam = UpdateChecker::UPGRADE_ERROR;
switch (code) {
case COMPLETION_CODE_SUCCESS:
case COMPLETION_CODE_SUCCESS_CLOSE_UI:
case COMPLETION_CODE_REBOOT:
case COMPLETION_CODE_REBOOT_NOTICE_ONLY:
case COMPLETION_CODE_RESTART_ALL_BROWSERS:
case COMPLETION_CODE_RESTART_BROWSER:
case COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY:
case COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY:
case COMPLETION_CODE_RUN_COMMAND:
if (upgrade_check_started_) {
if (new_version_available_) {
wParam = UpdateChecker::UPGRADE_IS_AVAILABLE;
} else {
wParam = UpdateChecker::UPGRADE_ALREADY_UP_TO_DATE;
}
}
break;
case COMPLETION_CODE_ERROR:
default:
wParam = UpdateChecker::UPGRADE_ERROR;
break;
}
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, wParam, 0);
// Stop the message-loop.
::PostQuitMessage(0);
return S_OK;
}
STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) {
progress_events_ = event_sink;
return S_OK;
}
static UpdateCheckJob *CreateInstance(
const UpdateChecker::CallbackInfo &info) {
return new UpdateCheckJob(info);
}
private:
explicit UpdateCheckJob(const UpdateChecker::CallbackInfo &info)
: reference_count_(0),
upgrade_check_started_(false),
new_version_available_(false),
callback_(info)
{}
volatile ULONG reference_count_;
bool upgrade_check_started_;
bool new_version_available_;
const UpdateChecker::CallbackInfo callback_;
CComPtr<IProgressWndEvents> progress_events_;
};
unsigned int __stdcall UpdateCheckWinThread(LPVOID lpThreadParameter) {
UpdateChecker::CallbackInfo *copied_data =
reinterpret_cast<UpdateChecker::CallbackInfo *>(lpThreadParameter);
const UpdateChecker::CallbackInfo info = *copied_data;
delete copied_data;
copied_data = NULL;
ScopedCOMInitializer com_init;
CComPtr<IJobObserver> job_observer(UpdateCheckJob::CreateInstance(info));
HRESULT hr = S_OK;
CComPtr<IGoogleUpdate> google_update;
hr = google_update.CoCreateInstance(CLSID_OnDemandMachineAppsClass);
if (hr != S_OK) {
return 0;
}
hr = google_update->CheckForUpdate(kOmahaGUID, job_observer);
// Message loop is required to drive COM RPC. This loop will be quitted
// by PostQuitMessage API from UpdateCheckJob::OnComplete.
MSG msg = {};
while (::GetMessage(&msg, NULL, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return 0;
}
bool BeginUpdateCheckWin(const UpdateChecker::CallbackInfo &info) {
UpdateChecker::CallbackInfo *copied_info = new UpdateChecker::CallbackInfo();
*copied_info = info;
unsigned int thread_id = 0;
ScopedHandle thread_handle(reinterpret_cast<HANDLE>(_beginthreadex(
NULL, 0, UpdateCheckWinThread, copied_info, 0, &thread_id)));
if (thread_handle.get() == NULL) {
delete copied_info;
copied_info = NULL;
return false;
}
return true;
}
class UpdateInvokerJob
: public IJobObserver {
public:
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void **ppvObject) {
if (::IsEqualGUID(riid, _ATL_IIDOF(IJobObserver))) {
*ppvObject = static_cast<IJobObserver *>(this);
this->AddRef();
return S_OK;
}
if (::IsEqualGUID(riid, IID_IUnknown)) {
*ppvObject = static_cast<IUnknown *>(this);
this->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef() {
return ::InterlockedIncrement(&reference_count_);
}
virtual ULONG STDMETHODCALLTYPE Release() {
const ULONG count = ::InterlockedDecrement(&reference_count_);
if (count == 0) {
delete this;
}
return count;
}
STDMETHOD(OnShow)() {
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_SHOW, 0);
return S_OK;
}
STDMETHOD(OnCheckingForUpdate)() {
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_CHECKING_FOR_UPDATE,
0);
return S_OK;
}
STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) {
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_UPDATE_AVAILABLE, 0);
return S_OK;
}
STDMETHOD(OnWaitingToDownload)() {
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_WAITING_TO_DOWNLOAD,
0);
return S_OK;
}
STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) {
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_DOWNLOADING, pos);
return S_OK;
}
STDMETHOD(OnWaitingToInstall)() {
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_WAITING_TO_INSTALL, 0);
return S_OK;
}
STDMETHOD(OnInstalling)() {
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_INSTALLING, 0);
return S_OK;
}
STDMETHOD(OnPause)() {
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_PAUSE, 0);
return S_OK;
}
STDMETHOD(OnComplete)(CompletionCodes code, const TCHAR* text) {
progress_events_ = NULL;
UpdateInvoker::CallbackOnCompleteLParam lParam = UpdateInvoker::JOB_FAILED;
switch (code) {
case COMPLETION_CODE_SUCCESS:
case COMPLETION_CODE_SUCCESS_CLOSE_UI:
case COMPLETION_CODE_REBOOT:
case COMPLETION_CODE_REBOOT_NOTICE_ONLY:
case COMPLETION_CODE_RESTART_ALL_BROWSERS:
case COMPLETION_CODE_RESTART_BROWSER:
case COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY:
case COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY:
case COMPLETION_CODE_RUN_COMMAND:
lParam = UpdateInvoker::JOB_SUCCEEDED;
break;
case COMPLETION_CODE_ERROR:
default:
lParam = UpdateInvoker::JOB_FAILED;
break;
}
::PostMessage(callback_.mesage_receiver_window,
callback_.mesage_id, UpdateInvoker::ON_COMPLETE, lParam);
// Stop the message-loop.
::PostQuitMessage(0);
return S_OK;
}
STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) {
progress_events_ = event_sink;
return S_OK;
}
static UpdateInvokerJob *CreateInstance(
const UpdateInvoker::CallbackInfo &info) {
return new UpdateInvokerJob(info);
}
private:
explicit UpdateInvokerJob(const UpdateInvoker::CallbackInfo &info)
: reference_count_(0),
callback_(info)
{}
volatile ULONG reference_count_;
const UpdateInvoker::CallbackInfo callback_;
CComPtr<IProgressWndEvents> progress_events_;
};
template <typename T>
HRESULT CoCreateInstanceAsAdmin(REFCLSID class_id, HWND window_handle,
T **interface_ptr) {
if (interface_ptr == NULL) {
return E_POINTER;
}
wchar_t class_id_as_string[MAX_PATH] = {};
::StringFromGUID2(class_id, class_id_as_string,
arraysize(class_id_as_string));
wstring elevation_moniker_name = L"Elevation:Administrator!new:";
elevation_moniker_name += class_id_as_string;
BIND_OPTS3 bind_opts;
memset(&bind_opts, 0, sizeof(bind_opts));
bind_opts.cbStruct = sizeof(bind_opts);
bind_opts.dwClassContext = CLSCTX_LOCAL_SERVER;
bind_opts.hwnd = window_handle;
return ::CoGetObject(elevation_moniker_name.c_str(), &bind_opts,
_ATL_IIDOF(T), reinterpret_cast<void**>(interface_ptr));
}
unsigned int __stdcall UpdateWinThread(LPVOID lpThreadParameter) {
UpdateInvoker::CallbackInfo *copied_data =
reinterpret_cast<UpdateInvoker::CallbackInfo *>(lpThreadParameter);
const UpdateInvoker::CallbackInfo info = *copied_data;
delete copied_data;
copied_data = NULL;
ScopedCOMInitializer com_init;
CComPtr<IJobObserver> job_observer(UpdateInvokerJob::CreateInstance(info));
HRESULT hr = S_OK;
CComPtr<IGoogleUpdate> google_update;
hr = CoCreateInstanceAsAdmin(CLSID_OnDemandMachineAppsClass,
info.mesage_receiver_window, &google_update);
if (hr != S_OK) {
return 0;
}
hr = google_update->Update(kOmahaGUID, job_observer);
// Message loop is required to drive COM RPC. This loop will be quitted
// by PostQuitMessage API from UpdateInvokerJob::OnComplete.
MSG msg = {};
while (::GetMessage(&msg, NULL, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return 0;
}
bool BeginUpdateWin(const UpdateInvoker::CallbackInfo &info) {
UpdateInvoker::CallbackInfo *copied_info = new UpdateInvoker::CallbackInfo();
*copied_info = info;
unsigned int thread_id = 0;
ScopedHandle thread_handle(reinterpret_cast<HANDLE>(_beginthreadex(
NULL, 0, UpdateWinThread, copied_info, 0, &thread_id)));
if (thread_handle.get() == NULL) {
delete copied_info;
copied_info = NULL;
return false;
}
return true;
}
#endif // OS_WIN && GOOGLE_JAPANESE_INPUT_BUILD
} // anonymouse namespace
bool UpdateChecker::BeginCheck(const CallbackInfo &info) {
#if !defined(OS_WIN) || !defined(GOOGLE_JAPANESE_INPUT_BUILD)
return false;
#else
return BeginUpdateCheckWin(info);
#endif
}
bool UpdateInvoker::BeginUpdate(const CallbackInfo &info) {
#if !defined(OS_WIN) || !defined(GOOGLE_JAPANESE_INPUT_BUILD)
return false;
#else
return BeginUpdateWin(info);
#endif
}
} // namespace mozc