Index: remoting/host/setup/daemon_controller_delegate_win.cc |
diff --git a/remoting/host/setup/daemon_controller_delegate_win.cc b/remoting/host/setup/daemon_controller_delegate_win.cc |
index e17432856f7ad47146196e64166db68f800a3156..c771b152332fcabdb4569f9a080842cf849f4912 100644 |
--- a/remoting/host/setup/daemon_controller_delegate_win.cc |
+++ b/remoting/host/setup/daemon_controller_delegate_win.cc |
@@ -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() { |