Commit 69cccfce authored by weitaosu's avatar weitaosu Committed by Commit bot

Move the |ElevatedDaemonController| implementation to |DaemonControllerDelegateWin|

Changes in this CL include:
1. Moved the daemon controller implemetation from |ElevatedDaemonController| to |DaemonControllerDelegateWin|
2. Removed all code related to COM activation in |DaemonControllerDelegateWin|.
3. Removed code related to SetOwnerWindow in |DaemonControllerDelegateWin|.
4. Removed code related to the pin confirmation dialog in |DaemonControllerDelegateWin|
5. Cleaned up the COM-styled code to make it adhere to the chromium coding styles.

BUG=453172

Review URL: https://codereview.chromium.org/877343004

Cr-Commit-Position: refs/heads/master@{#315129}
parent c2e11810
......@@ -5,60 +5,211 @@
#include "remoting/host/setup/daemon_controller_delegate_win.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h"
#include "remoting/base/scoped_sc_handle_win.h"
#include "remoting/host/branding.h"
// chromoting_lib.h contains MIDL-generated declarations.
#include "remoting/host/chromoting_lib.h"
#include "remoting/host/host_config.h"
#include "remoting/host/usage_stats_consent.h"
using base::win::ScopedBstr;
using base::win::ScopedComPtr;
#include "remoting/host/win/security_descriptor.h"
namespace remoting {
namespace {
// ProgID of the daemon controller.
const wchar_t kDaemonController[] =
L"ChromotingElevatedController.ElevatedController";
// The maximum size of the configuration file. "1MB ought to be enough" for any
// reasonable configuration we will ever need. 1MB is low enough to make
// the probability of out of memory situation fairly low. OOM is still possible
// and we will crash if it occurs.
const size_t kMaxConfigFileSize = 1024 * 1024;
// The host configuration file name.
const base::FilePath::CharType kConfigFileName[] =
FILE_PATH_LITERAL("host.json");
// The unprivileged configuration file name.
const base::FilePath::CharType kUnprivilegedConfigFileName[] =
FILE_PATH_LITERAL("host_unprivileged.json");
// The extension for the temporary file.
const base::FilePath::CharType kTempFileExtension[] =
FILE_PATH_LITERAL("json~");
// The host configuration file security descriptor that enables full access to
// Local System and built-in administrators only.
const char kConfigFileSecurityDescriptor[] =
"O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)";
const char kUnprivilegedConfigFileSecurityDescriptor[] =
"O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GR;;;AU)";
// Configuration keys.
// The configuration keys that cannot be specified in UpdateConfig().
const char* const kReadonlyKeys[] = {
kHostIdConfigPath, kHostOwnerConfigPath, kHostOwnerEmailConfigPath,
kXmppLoginConfigPath };
// The configuration keys whose values may be read by GetConfig().
const char* const kUnprivilegedConfigKeys[] = {
kHostIdConfigPath, kXmppLoginConfigPath };
// Reads and parses the configuration file up to |kMaxConfigFileSize| in
// size.
bool ReadConfig(const base::FilePath& filename,
scoped_ptr<base::DictionaryValue>* config_out) {
std::string file_content;
if (!base::ReadFileToString(filename, &file_content, kMaxConfigFileSize)) {
PLOG(ERROR) << "Failed to read '" << filename.value() << "'.";
return false;
}
// The COM elevation moniker for the Elevated Controller.
const wchar_t kDaemonControllerElevationMoniker[] =
L"Elevation:Administrator!new:"
L"ChromotingElevatedController.ElevatedController";
// Parse the JSON configuration, expecting it to contain a dictionary.
scoped_ptr<base::Value> value(
base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS));
// The maximum duration of keeping a reference to a privileged instance of
// the Daemon Controller. This effectively reduces number of UAC prompts a user
// sees.
const int kPrivilegedTimeoutSec = 5 * 60;
base::DictionaryValue* dictionary;
if (!value || !value->GetAsDictionary(&dictionary)) {
LOG(ERROR) << "Failed to parse '" << filename.value() << "'.";
return false;
}
// The maximum duration of keeping a reference to an unprivileged instance of
// the Daemon Controller. This interval should not be too long. If upgrade
// happens while there is a live reference to a Daemon Controller instance
// the old binary still can be used. So dropping the references often makes sure
// that the old binary will go away sooner.
const int kUnprivilegedTimeoutSec = 60;
value.release();
config_out->reset(dictionary);
return true;
}
void ConfigToString(const base::DictionaryValue& config, ScopedBstr* out) {
std::string config_str;
base::JSONWriter::Write(&config, &config_str);
ScopedBstr config_scoped_bstr(base::UTF8ToUTF16(config_str).c_str());
out->Swap(config_scoped_bstr);
base::FilePath GetTempLocationFor(const base::FilePath& filename) {
return filename.ReplaceExtension(kTempFileExtension);
}
// Writes a config file to a temporary location.
bool WriteConfigFileToTemp(const base::FilePath& filename,
const char* security_descriptor,
const std::string& content) {
// Create the security descriptor for the configuration file.
ScopedSd sd = ConvertSddlToSd(security_descriptor);
if (!sd) {
PLOG(ERROR)
<< "Failed to create a security descriptor for the configuration file";
return false;
}
SECURITY_ATTRIBUTES security_attributes = {0};
security_attributes.nLength = sizeof(security_attributes);
security_attributes.lpSecurityDescriptor = sd.get();
security_attributes.bInheritHandle = FALSE;
// Create a temporary file and write configuration to it.
base::FilePath tempname = GetTempLocationFor(filename);
base::win::ScopedHandle file(
CreateFileW(tempname.value().c_str(),
GENERIC_WRITE,
0,
&security_attributes,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
nullptr));
if (!file.IsValid()) {
PLOG(ERROR) << "Failed to create '" << filename.value() << "'";
return false;
}
DWORD written;
if (!::WriteFile(file.Get(), content.c_str(), content.length(),
&written, nullptr)) {
PLOG(ERROR) << "Failed to write to '" << filename.value() << "'";
return false;
}
return true;
}
// Moves a config file from its temporary location to its permanent location.
bool MoveConfigFileFromTemp(const base::FilePath& filename) {
// Now that the configuration is stored successfully replace the actual
// configuration file.
base::FilePath tempname = GetTempLocationFor(filename);
if (!MoveFileExW(tempname.value().c_str(),
filename.value().c_str(),
MOVEFILE_REPLACE_EXISTING)) {
PLOG(ERROR) << "Failed to rename '" << tempname.value() << "' to '"
<< filename.value() << "'";
return false;
}
return true;
}
// Writes the configuration file up to |kMaxConfigFileSize| in size.
bool WriteConfig(const std::string& content) {
if (content.length() > kMaxConfigFileSize) {
return false;
}
// Extract the configuration data that the user will verify.
scoped_ptr<base::Value> config_value(base::JSONReader::Read(content));
if (!config_value.get()) {
return false;
}
base::DictionaryValue* config_dict = nullptr;
if (!config_value->GetAsDictionary(&config_dict)) {
return false;
}
std::string email;
if (!config_dict->GetString(kHostOwnerEmailConfigPath, &email) &&
!config_dict->GetString(kHostOwnerConfigPath, &email) &&
!config_dict->GetString(kXmppLoginConfigPath, &email)) {
return false;
}
std::string host_id, host_secret_hash;
if (!config_dict->GetString(kHostIdConfigPath, &host_id) ||
!config_dict->GetString(kHostSecretHashConfigPath, &host_secret_hash)) {
return false;
}
// Extract the unprivileged fields from the configuration.
base::DictionaryValue unprivileged_config_dict;
for (int i = 0; i < arraysize(kUnprivilegedConfigKeys); ++i) {
const char* key = kUnprivilegedConfigKeys[i];
base::string16 value;
if (config_dict->GetString(key, &value)) {
unprivileged_config_dict.SetString(key, value);
}
}
std::string unprivileged_config_str;
base::JSONWriter::Write(&unprivileged_config_dict, &unprivileged_config_str);
// Write the full configuration file to a temporary location.
base::FilePath full_config_file_path =
remoting::GetConfigDir().Append(kConfigFileName);
if (!WriteConfigFileToTemp(full_config_file_path,
kConfigFileSecurityDescriptor,
content)) {
return false;
}
// Write the unprivileged configuration file to a temporary location.
base::FilePath unprivileged_config_file_path =
remoting::GetConfigDir().Append(kUnprivilegedConfigFileName);
if (!WriteConfigFileToTemp(unprivileged_config_file_path,
kUnprivilegedConfigFileSecurityDescriptor,
unprivileged_config_str)) {
return false;
}
// Move the full and unprivileged configuration files to their permanent
// locations.
return MoveConfigFileFromTemp(full_config_file_path) &&
MoveConfigFileFromTemp(unprivileged_config_file_path);
}
DaemonController::State ConvertToDaemonState(DWORD service_state) {
......@@ -87,76 +238,123 @@ DaemonController::State ConvertToDaemonState(DWORD service_state) {
}
}
DWORD OpenService(ScopedScHandle* service_out) {
ScopedScHandle OpenService(DWORD access) {
// Open the service and query its current state.
ScopedScHandle scmanager(
::OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
if (!scmanager.IsValid()) {
DWORD error = GetLastError();
PLOG(ERROR) << "Failed to connect to the service control manager";
return error;
return ScopedScHandle();
}
ScopedScHandle service(::OpenServiceW(scmanager.Get(), kWindowsServiceName,
SERVICE_QUERY_STATUS));
access));
if (!service.IsValid()) {
DWORD error = GetLastError();
if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
PLOG(ERROR) << "Failed to open to the '" << kWindowsServiceName
<< "' service";
}
return error;
PLOG(ERROR) << "Failed to open to the '" << kWindowsServiceName
<< "' service";
}
service_out->Set(service.Take());
return ERROR_SUCCESS;
return service.Pass();
}
DaemonController::AsyncResult HResultToAsyncResult(
HRESULT hr) {
if (SUCCEEDED(hr)) {
return DaemonController::RESULT_OK;
} else if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
return DaemonController::RESULT_CANCELLED;
} else {
// TODO(sergeyu): Report other errors to the webapp once it knows
// how to handle them.
return DaemonController::RESULT_FAILED;
}
void InvokeCompletionCallback(
const DaemonController::CompletionCallback& done, bool success) {
DaemonController::AsyncResult async_result =
success ? DaemonController::RESULT_OK : DaemonController::RESULT_FAILED;
done.Run(async_result);
}
void InvokeCompletionCallback(
const DaemonController::CompletionCallback& done, HRESULT hr) {
done.Run(HResultToAsyncResult(hr));
bool SetConfig(const std::string& config) {
// Determine the config directory path and create it if necessary.
base::FilePath config_dir = remoting::GetConfigDir();
if (!base::CreateDirectory(config_dir)) {
PLOG(ERROR) << "Failed to create the config directory.";
return false;
}
return WriteConfig(config);
}
HWND GetTopLevelWindow(HWND window) {
if (window == nullptr) {
return nullptr;
bool StartDaemon() {
DWORD access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
SERVICE_START | SERVICE_STOP;
ScopedScHandle service = OpenService(access);
if (!service.IsValid())
return false;
// Change the service start type to 'auto'.
if (!::ChangeServiceConfigW(service.Get(),
SERVICE_NO_CHANGE,
SERVICE_AUTO_START,
SERVICE_NO_CHANGE,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr)) {
PLOG(ERROR) << "Failed to change the '" << kWindowsServiceName
<< "'service start type to 'auto'";
return false;
}
for (;;) {
LONG style = GetWindowLong(window, GWL_STYLE);
if ((style & WS_OVERLAPPEDWINDOW) == WS_OVERLAPPEDWINDOW ||
(style & WS_POPUP) == WS_POPUP) {
return window;
}
// Start the service.
if (!StartService(service.Get(), 0, nullptr)) {
DWORD error = GetLastError();
if (error != ERROR_SERVICE_ALREADY_RUNNING) {
LOG(ERROR) << "Failed to start the '" << kWindowsServiceName
<< "'service: " << error;
HWND parent = GetAncestor(window, GA_PARENT);
if (parent == nullptr) {
return window;
return false;
}
}
window = parent;
return true;
}
bool StopDaemon() {
DWORD access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
SERVICE_START | SERVICE_STOP;
ScopedScHandle service = OpenService(access);
if (!service.IsValid())
return false;
// Change the service start type to 'manual'.
if (!::ChangeServiceConfigW(service.Get(),
SERVICE_NO_CHANGE,
SERVICE_DEMAND_START,
SERVICE_NO_CHANGE,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr)) {
PLOG(ERROR) << "Failed to change the '" << kWindowsServiceName
<< "'service start type to 'manual'";
return false;
}
// Stop the service.
SERVICE_STATUS status;
if (!ControlService(service.Get(), SERVICE_CONTROL_STOP, &status)) {
DWORD error = GetLastError();
if (error != ERROR_SERVICE_NOT_ACTIVE) {
LOG(ERROR) << "Failed to stop the '" << kWindowsServiceName
<< "'service: " << error;
return false;
}
}
return true;
}
} // namespace
DaemonControllerDelegateWin::DaemonControllerDelegateWin()
: control_is_elevated_(false),
window_handle_(nullptr) {
DaemonControllerDelegateWin::DaemonControllerDelegateWin() {
}
DaemonControllerDelegateWin::~DaemonControllerDelegateWin() {
......@@ -166,111 +364,78 @@ DaemonController::State DaemonControllerDelegateWin::GetState() {
if (base::win::GetVersion() < base::win::VERSION_XP) {
return DaemonController::STATE_NOT_IMPLEMENTED;
}
// TODO(alexeypa): Make the thread alertable, so we can switch to APC
// notifications rather than polling.
ScopedScHandle service;
DWORD error = OpenService(&service);
switch (error) {
case ERROR_SUCCESS: {
SERVICE_STATUS status;
if (::QueryServiceStatus(service.Get(), &status)) {
return ConvertToDaemonState(status.dwCurrentState);
} else {
PLOG(ERROR) << "Failed to query the state of the '"
<< kWindowsServiceName << "' service";
return DaemonController::STATE_UNKNOWN;
}
break;
}
case ERROR_SERVICE_DOES_NOT_EXIST:
return DaemonController::STATE_NOT_INSTALLED;
default:
return DaemonController::STATE_UNKNOWN;
ScopedScHandle service = OpenService(SERVICE_QUERY_STATUS);
if (!service.IsValid())
return DaemonController::STATE_UNKNOWN;
SERVICE_STATUS status;
if (!::QueryServiceStatus(service.Get(), &status)) {
PLOG(ERROR) << "Failed to query the state of the '"
<< kWindowsServiceName << "' service";
return DaemonController::STATE_UNKNOWN;
}
return ConvertToDaemonState(status.dwCurrentState);
}
scoped_ptr<base::DictionaryValue> DaemonControllerDelegateWin::GetConfig() {
// Configure and start the Daemon Controller if it is installed already.
HRESULT hr = ActivateController();
if (FAILED(hr))
return nullptr;
// Get the host configuration.
ScopedBstr host_config;
hr = control_->GetConfig(host_config.Receive());
if (FAILED(hr))
return nullptr;
base::FilePath config_dir = remoting::GetConfigDir();
// Parse the string into a dictionary.
base::string16 file_content(
static_cast<BSTR>(host_config), host_config.Length());
scoped_ptr<base::Value> config(
base::JSONReader::Read(base::UTF16ToUTF8(file_content),
base::JSON_ALLOW_TRAILING_COMMAS));
if (!config || !config->IsType(base::Value::TYPE_DICTIONARY))
// Read the unprivileged part of host configuration.
scoped_ptr<base::DictionaryValue> config;
if (!ReadConfig(config_dir.Append(kUnprivilegedConfigFileName), &config))
return nullptr;
return make_scoped_ptr(static_cast<base::DictionaryValue*>(config.release()));
return config;
}
void DaemonControllerDelegateWin::UpdateConfig(
scoped_ptr<base::DictionaryValue> config,
const DaemonController::CompletionCallback& done) {
HRESULT hr = ActivateElevatedController();
if (FAILED(hr)) {
InvokeCompletionCallback(done, hr);
return;
// Check for bad keys.
for (int i = 0; i < arraysize(kReadonlyKeys); ++i) {
if (config->HasKey(kReadonlyKeys[i])) {
LOG(ERROR) << "Cannot update config: '" << kReadonlyKeys[i]
<< "' is read only.";
InvokeCompletionCallback(done, false);
return;
}
}
// Update the configuration.
ScopedBstr config_str(nullptr);
ConfigToString(*config, &config_str);
if (config_str == nullptr) {
InvokeCompletionCallback(done, E_OUTOFMEMORY);
// Get the old config.
base::FilePath config_dir = remoting::GetConfigDir();
scoped_ptr<base::DictionaryValue> config_old;
if (!ReadConfig(config_dir.Append(kConfigFileName), &config_old)) {
InvokeCompletionCallback(done, false);
return;
}
// Make sure that the PIN confirmation dialog is focused properly.
hr = control_->SetOwnerWindow(
reinterpret_cast<LONG_PTR>(GetTopLevelWindow(window_handle_)));
if (FAILED(hr)) {
InvokeCompletionCallback(done, hr);
return;
}
// Merge items from the given config into the old config.
config_old->MergeDictionary(config.release());
// Write the updated config.
std::string config_updated_str;
base::JSONWriter::Write(config_old.get(), &config_updated_str);
bool result = WriteConfig(config_updated_str);
hr = control_->UpdateConfig(config_str);
InvokeCompletionCallback(done, hr);
InvokeCompletionCallback(done, result);
}
void DaemonControllerDelegateWin::Stop(
const DaemonController::CompletionCallback& done) {
HRESULT hr = ActivateElevatedController();
if (SUCCEEDED(hr))
hr = control_->StopDaemon();
bool result = StopDaemon();
InvokeCompletionCallback(done, hr);
InvokeCompletionCallback(done, result);
}
void DaemonControllerDelegateWin::SetWindow(void* window_handle) {
window_handle_ = reinterpret_cast<HWND>(window_handle);
}
std::string DaemonControllerDelegateWin::GetVersion() {
// Configure and start the Daemon Controller if it is installed already.
HRESULT hr = ActivateController();
if (FAILED(hr))
return std::string();
// Get the version string.
ScopedBstr version;
hr = control_->GetVersion(version.Receive());
if (FAILED(hr))
return std::string();
return base::UTF16ToUTF8(
base::string16(static_cast<BSTR>(version), version.Length()));
// TODO (weitaosu): Remove this as GetVersion is not used anymore.
return std::string();
}
DaemonController::UsageStatsConsent
......@@ -280,154 +445,48 @@ DaemonControllerDelegateWin::GetUsageStatsConsent() {
consent.allowed = false;
consent.set_by_policy = false;
// Activate the Daemon Controller and see if it supports |IDaemonControl2|.
HRESULT hr = ActivateController();
if (FAILED(hr)) {
// The host is not installed yet. Assume that the user didn't consent to
// collecting crash dumps.
return consent;
}
if (control2_.get() == nullptr) {
// The host is installed and does not support crash dump reporting.
return consent;
}
// Get the recorded user's consent.
BOOL allowed;
BOOL set_by_policy;
hr = control2_->GetUsageStatsConsent(&allowed, &set_by_policy);
if (FAILED(hr)) {
// If the user's consent is not recorded yet, assume that the user didn't
// consent to collecting crash dumps.
return consent;
bool allowed;
bool set_by_policy;
// If the user's consent is not recorded yet, assume that the user didn't
// consent to collecting crash dumps.
if (remoting::GetUsageStatsConsent(&allowed, &set_by_policy)) {
consent.allowed = allowed;
consent.set_by_policy = set_by_policy;
}
consent.allowed = !!allowed;
consent.set_by_policy = !!set_by_policy;
return consent;
}
HRESULT DaemonControllerDelegateWin::ActivateController() {
if (!control_.get()) {
CLSID class_id;
HRESULT hr = CLSIDFromProgID(kDaemonController, &class_id);
if (FAILED(hr)) {
return hr;
}
hr = CoCreateInstance(class_id, nullptr, CLSCTX_LOCAL_SERVER,
IID_IDaemonControl, control_.ReceiveVoid());
if (FAILED(hr)) {
return hr;
}
// Ignore the error. IID_IDaemonControl2 is optional.
control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid());
// Release |control_| upon expiration of the timeout.
release_timer_.reset(new base::OneShotTimer<DaemonControllerDelegateWin>());
release_timer_->Start(FROM_HERE,
base::TimeDelta::FromSeconds(kUnprivilegedTimeoutSec),
this,
&DaemonControllerDelegateWin::ReleaseController);
}
return S_OK;
}
HRESULT DaemonControllerDelegateWin::ActivateElevatedController() {
// The COM elevation is supported on Vista and above.
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return ActivateController();
// Release an unprivileged instance of the daemon controller if any.
if (!control_is_elevated_)
ReleaseController();
if (!control_.get()) {
BIND_OPTS3 bind_options;
memset(&bind_options, 0, sizeof(bind_options));
bind_options.cbStruct = sizeof(bind_options);
bind_options.hwnd = GetTopLevelWindow(window_handle_);
bind_options.dwClassContext = CLSCTX_LOCAL_SERVER;
HRESULT hr = ::CoGetObject(
kDaemonControllerElevationMoniker,
&bind_options,
IID_IDaemonControl,
control_.ReceiveVoid());
if (FAILED(hr)) {
return hr;
}
// Ignore the error. IID_IDaemonControl2 is optional.
control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid());
// Note that we hold a reference to an elevated instance now.
control_is_elevated_ = true;
// Release |control_| upon expiration of the timeout.
release_timer_.reset(new base::OneShotTimer<DaemonControllerDelegateWin>());
release_timer_->Start(FROM_HERE,
base::TimeDelta::FromSeconds(kPrivilegedTimeoutSec),
this,
&DaemonControllerDelegateWin::ReleaseController);
}
return S_OK;
}
void DaemonControllerDelegateWin::ReleaseController() {
control_.Release();
control2_.Release();
release_timer_.reset();
control_is_elevated_ = false;
}
void DaemonControllerDelegateWin::SetConfigAndStart(
scoped_ptr<base::DictionaryValue> config,
bool consent,
const DaemonController::CompletionCallback& done) {
HRESULT hr = ActivateElevatedController();
if (FAILED(hr)) {
InvokeCompletionCallback(done, hr);
return;
}
// Record the user's consent.
if (control2_.get()) {
hr = control2_->SetUsageStatsConsent(consent);
if (FAILED(hr)) {
InvokeCompletionCallback(done, hr);
return;
}
if (!remoting::SetUsageStatsConsent(consent)) {
InvokeCompletionCallback(done, false);
return;
}
// Set the configuration.
ScopedBstr config_str(nullptr);
ConfigToString(*config, &config_str);
if (config_str == nullptr) {
InvokeCompletionCallback(done, E_OUTOFMEMORY);
return;
}
std::string config_str;
base::JSONWriter::Write(config.release(), &config_str);
hr = control_->SetOwnerWindow(
reinterpret_cast<LONG_PTR>(GetTopLevelWindow(window_handle_)));
if (FAILED(hr)) {
InvokeCompletionCallback(done, hr);
// Determine the config directory path and create it if necessary.
base::FilePath config_dir = remoting::GetConfigDir();
if (!base::CreateDirectory(config_dir)) {
PLOG(ERROR) << "Failed to create the config directory.";
InvokeCompletionCallback(done, false);
return;
}
hr = control_->SetConfig(config_str);
if (FAILED(hr)) {
InvokeCompletionCallback(done, hr);
if (!WriteConfig(config_str)) {
InvokeCompletionCallback(done, false);
return;
}
// Start daemon.
hr = control_->StartDaemon();
InvokeCompletionCallback(done, hr);
InvokeCompletionCallback(done, StartDaemon());
}
scoped_refptr<DaemonController> DaemonController::Create() {
......
......@@ -5,11 +5,6 @@
#ifndef REMOTING_HOST_SETUP_DAEMON_CONTROLLER_DELEGATE_WIN_H_
#define REMOTING_HOST_SETUP_DAEMON_CONTROLLER_DELEGATE_WIN_H_
#include "base/memory/scoped_ptr.h"
#include "base/timer/timer.h"
#include "base/win/scoped_comptr.h"
// chromoting_lib.h contains MIDL-generated declarations.
#include "remoting/host/chromoting_lib.h"
#include "remoting/host/setup/daemon_controller.h"
namespace remoting {
......@@ -36,33 +31,6 @@ class DaemonControllerDelegateWin : public DaemonController::Delegate {
virtual std::string GetVersion() override;
virtual DaemonController::UsageStatsConsent GetUsageStatsConsent() override;
private:
// Activates an unprivileged instance of the daemon controller and caches it.
HRESULT ActivateController();
// Activates an instance of the daemon controller and caches it. If COM
// Elevation is supported (Vista+) the activated instance is elevated,
// otherwise it is activated under credentials of the caller.
HRESULT ActivateElevatedController();
// Releases the cached instance of the controller.
void ReleaseController();
// |control_| and |control2_| hold references to an instance of the daemon
// controller to prevent a UAC prompt on every operation.
base::win::ScopedComPtr<IDaemonControl> control_;
base::win::ScopedComPtr<IDaemonControl2> control2_;
// True if |control_| holds a reference to an elevated instance of the daemon
// controller.
bool control_is_elevated_;
// This timer is used to release |control_| after a timeout.
scoped_ptr<base::OneShotTimer<DaemonControllerDelegateWin> > release_timer_;
// Handle of the plugin window.
HWND window_handle_;
DISALLOW_COPY_AND_ASSIGN(DaemonControllerDelegateWin);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment