blob: 893d4602592a04a593d568f724c4bb7c1d802516 [file] [log] [blame]
// Copyright 2008-2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
//
// OneClick worker to handle threads and manage callbacks to the browser.
#include "omaha/plugins/oneclick_worker.h"
#include <dispex.h>
#include <wininet.h>
#include <atlbase.h>
#include <atlcom.h>
#include "omaha/common/app_util.h"
#include "omaha/common/atl_regexp.h"
#include "omaha/common/const_cmd_line.h"
#include "omaha/common/error.h"
#include "omaha/common/logging.h"
#include "omaha/common/scoped_any.h"
#include "omaha/common/string.h"
#include "omaha/goopdate/command_line_builder.h"
#include "omaha/goopdate/goopdate_utils.h"
#include "omaha/goopdate/webplugin_utils.h"
#include "omaha/worker/application_manager.h"
#define INITGUID
#include <guiddef.h> // NOLINT
namespace omaha {
const TCHAR* site_lock_pattern_strings[] = {
_T("^https?://(gears)|(mail)|(tools)|(www)|(desktop)\\.google\\.com/"),
_T("^https?://www\\.google\\.(ad)|(bg)|(ca)|(cn)|(cz)|(de)|(es)|(fi)|(fr)|(gr)|(hr)|(hu)|(it)|(ki)|(kr)|(lt)|(lv)|(nl)|(no)|(pl)|(pt)|(ro)|(ru)|(sk)|(sg)|(sl)|(sr)|(vn)/"), // NOLINT
_T("^https?://www\\.google\\.co\\.(hu)|(id)|(il)|(it)|(jp)|(kr)|(th)|(uk)/"),
_T("^https?://www\\.google\\.com\\.(ar)|(au)|(br)|(cn)|(et)|(gr)|(hr)|(ki)|(lv)|(om)|(pl)|(pt)|(ru)|(sg)|(sv)|(tr)|(vn)/"), // NOLINT
};
typedef std::vector<AtlRegExp*> HostRegexp;
class SiteLockPatterns {
public:
SiteLockPatterns() {}
~SiteLockPatterns();
bool AddPattern(const CString& host_pattern);
bool Match(const CString& url);
private:
HostRegexp hosts_;
};
bool SiteLockPatterns::AddPattern(const CString& host_pattern) {
scoped_ptr<AtlRegExp> regex(new AtlRegExp);
REParseError error = regex->Parse(host_pattern);
if (error != REPARSE_ERROR_OK) {
return false;
}
hosts_.push_back(regex.release());
return true;
}
bool SiteLockPatterns::Match(const CString& url) {
if (url.IsEmpty()) {
return false;
}
ASSERT1(!hosts_.empty());
for (size_t i = 0; i < hosts_.size(); ++i) {
AtlMatchContext url_match;
if (hosts_[i]->Match(url, &url_match)) {
return true;
}
}
return false;
}
SiteLockPatterns::~SiteLockPatterns() {
for (size_t i = 0; i < hosts_.size(); ++i) {
delete hosts_[i];
}
}
OneClickWorker::OneClickWorker() {
CORE_LOG(L2, (_T("OneClickWorker::OneClickWorker()")));
site_lock_patterns_.reset(new SiteLockPatterns());
// TODO(Omaha): Download new patterns on the fly.
for (int i = 0; i < arraysize(site_lock_pattern_strings); ++i) {
VERIFY1(site_lock_patterns_->AddPattern(site_lock_pattern_strings[i]));
}
CString update_dev_host_pattern;
if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
kRegValueOneClickHostPattern,
&update_dev_host_pattern)) &&
!update_dev_host_pattern.IsEmpty()) {
VERIFY1(site_lock_patterns_->AddPattern(update_dev_host_pattern));
}
}
OneClickWorker::~OneClickWorker() {
CORE_LOG(L2, (_T("OneClickWorker::~OneClickWorker()")));
}
HRESULT OneClickWorker::Initialize() {
CORE_LOG(L2, (_T("[OneClickWorker::Initialize]")));
return S_OK;
}
HRESULT OneClickWorker::Shutdown() {
CORE_LOG(L2, (_T("[OneClickWorker::Shutdown]")));
return S_OK;
}
bool OneClickWorker::InApprovedDomain() {
ASSERT1(!browser_url_.IsEmpty());
return site_lock_patterns_->Match(browser_url_);
}
HRESULT OneClickWorker::DoOneClickInstall(
const TCHAR* cmd_line_args,
OneClickBrowserCallback* browser_callback) {
CORE_LOG(L2, (_T("[OneClickWorker::DoOneClickInstall]")
_T("[cmd_line_args=%s][browser_url=%s]"),
cmd_line_args, browser_url_));
ASSERT1(browser_callback);
HRESULT hr = DoOneClickInstallInternal(cmd_line_args);
if (SUCCEEDED(hr)) {
browser_callback->DoSuccessCallback();
} else {
CORE_LOG(LE, (_T("[DoOneClickInstallInternal failed][0x%x]"), hr));
browser_callback->DoFailureCallback(hr);
}
// Return success in all cases. The failure callback has already been called
// above, and we don't want to cause a failure path to be called again when
// the JavaScript catches the exception.
return S_OK;
}
HRESULT OneClickWorker::DoOneClickInstall2(const TCHAR* extra_args) {
CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
builder.set_extra_args(extra_args);
return DoOneClickInstallInternal(builder.GetCommandLineArgs());
}
HRESULT OneClickWorker::DoOneClickInstallInternal(const TCHAR* cmd_line_args) {
if (!InApprovedDomain()) {
return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
}
#ifdef _DEBUG
// If the args are exactly __DIRECTNOTIFY__ then just fire the event
// out of this thread. This allows for easy testing of the
// browser interface without requiring launch of
// google_update.exe.
if (0 == _wcsicmp(L"__DIRECTNOTIFY__", cmd_line_args)) {
return S_OK;
}
#endif
HRESULT hr = webplugin_utils::VerifyResourceLanguage(cmd_line_args);
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[VerifyResourceLanguage failed][0x%08x]"), hr));
return hr;
}
CString url_domain;
hr = GetUrlDomain(browser_url_, &url_domain);
if (FAILED(hr)) {
return hr;
}
CString url_domain_encoded;
CString cmd_line_args_encoded;
hr = StringEscape(url_domain, false, &url_domain_encoded);
if (FAILED(hr)) {
return hr;
}
hr = StringEscape(cmd_line_args, false, &cmd_line_args_encoded);
if (FAILED(hr)) {
return hr;
}
CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN);
builder.set_webplugin_url_domain(url_domain_encoded);
builder.set_webplugin_args(cmd_line_args_encoded);
builder.set_install_source(kCmdLineInstallSource_OneClick);
CString final_cmd_line_args = builder.GetCommandLineArgs();
CORE_LOG(L2, (_T("[OneClickWorker::DoOneClickInstallInternal]")
_T("[Final command line params: %s]"),
final_cmd_line_args));
scoped_process process_goopdate;
hr = goopdate_utils::StartGoogleUpdateWithArgs(
goopdate_utils::IsRunningFromOfficialGoopdateDir(true),
final_cmd_line_args,
address(process_goopdate));
if (FAILED(hr)) {
CORE_LOG(LE, (_T("[OneClickWorker::DoOneClickInstallInternal]")
_T("[Failed StartGoogleUpdateWithArgs: 0x%x"),
hr));
return hr;
}
return S_OK;
}
HRESULT OneClickWorker::GetInstalledVersion(const TCHAR* guid_string,
bool is_machine,
CString* version_string) {
CORE_LOG(L2, (_T("[GoopdateCtrl::GetInstalledVersion][%s][%d]"),
guid_string, is_machine));
if (!InApprovedDomain()) {
return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
}
ASSERT1(version_string);
version_string->Empty();
AppManager app_manager(is_machine);
ProductData product_data;
HRESULT hr = app_manager.ReadProductDataFromStore(StringToGuid(guid_string),
&product_data);
if (SUCCEEDED(hr) && !product_data.app_data().is_uninstalled()) {
*version_string = product_data.app_data().version();
}
return S_OK;
}
HRESULT OneClickWorker::GetOneClickVersion(long* version) { // NOLINT
CORE_LOG(L2, (_T("[OneClickWorker::GetOneClickVersion]")));
if (!InApprovedDomain()) {
return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
}
if (!version) {
return E_POINTER;
}
*version = atoi(ACTIVEX_VERSION_ANSI); // NOLINT
return S_OK;
}
HRESULT OneClickWorker::GetUrlDomain(const CString& url, CString* url_domain) {
ASSERT1(url_domain);
url_domain->Empty();
URL_COMPONENTS urlComponents = {0};
urlComponents.dwStructSize = sizeof(urlComponents);
urlComponents.dwSchemeLength = 1;
urlComponents.dwHostNameLength = 1;
if (!::InternetCrackUrl(url, 0, 0, &urlComponents)) {
HRESULT hr = HRESULTFromLastError();
CORE_LOG(L2, (_T("[OneClickWorker failed InternetCrackUrl hr=0x%x]"), hr));
return hr;
}
CString scheme(urlComponents.lpszScheme, urlComponents.dwSchemeLength);
CString host_name(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
ASSERT1(!scheme.IsEmpty());
ASSERT1(!host_name.IsEmpty());
url_domain->Format(_T("%s://%s/"), scheme, host_name);
return S_OK;
}
} // namespace omaha