Index: remoting/host/elevated_controller_win.cc |
diff --git a/remoting/host/elevated_controller_win.cc b/remoting/host/elevated_controller_win.cc |
index 708bed29f74a240cd0aeb60d4cdd150359838128..fbeeea9f2e6ca98c72984fdd857c8129a2211b69 100644 |
--- a/remoting/host/elevated_controller_win.cc |
+++ b/remoting/host/elevated_controller_win.cc |
@@ -4,11 +4,89 @@ |
#include "remoting/host/elevated_controller_win.h" |
+#include "base/file_util.h" |
+#include "base/logging.h" |
+#include "base/json/json_reader.h" |
+#include "base/json/json_writer.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/path_service.h" |
+#include "base/utf_string_conversions.h" |
+#include "base/values.h" |
+#include "base/win/scoped_handle.h" |
+#include "remoting/host/branding.h" |
+ |
// MIDL-generated definitions. |
-#include <elevated_controller_i.c> |
+#include "elevated_controller_i.c" |
+ |
+namespace { |
+ |
+// ReadConfig() filters the configuration file stripping all variables except of |
+// the following two. |
+const char kHostId[] = "host_id"; |
+const char kXmppLogin[] = "xmpp_login"; |
+ |
+// Names of the configuration files. |
+const FilePath::CharType kAuthConfigFilename[] = FILE_PATH_LITERAL("auth.json"); |
+const FilePath::CharType kHostConfigFilename[] = FILE_PATH_LITERAL("host.json"); |
+ |
+// TODO(alexeypa): Remove the hardcoded undocumented paths and store |
+// the configuration in the registry. |
+#ifdef OFFICIAL_BUILD |
+const FilePath::CharType kConfigDir[] = FILE_PATH_LITERAL( |
+ "config\\systemprofile\\AppData\\Local\\Google\\Chrome Remote Desktop"); |
+#else |
+const FilePath::CharType kConfigDir[] = |
+ FILE_PATH_LITERAL("config\\systemprofile\\AppData\\Local\\Chromoting"); |
+#endif |
+ |
+// Reads and parses a JSON configuration file. |
+HRESULT ReadConfig(const FilePath& filename, |
+ scoped_ptr<base::DictionaryValue>* config_out) { |
+ // TODO(alexeypa): Remove 64KB limitation. |
+ const size_t kMaxConfigFileSize = 64 * 1024; |
+ |
+ // Read raw data from the configuration file. |
+ base::win::ScopedHandle file( |
+ CreateFileW(filename.value().c_str(), |
+ GENERIC_READ, |
+ FILE_SHARE_READ | FILE_SHARE_WRITE, |
+ NULL, |
+ OPEN_EXISTING, |
+ FILE_FLAG_SEQUENTIAL_SCAN, |
+ NULL)); |
+ |
+ if (!file.IsValid()) { |
+ DWORD error = GetLastError(); |
+ LOG_GETLASTERROR(ERROR) |
+ << "Failed to read '" << filename.value() << "'"; |
+ return HRESULT_FROM_WIN32(error); |
+ } |
+ |
+ std::vector<char> buffer(kMaxConfigFileSize); |
+ DWORD size = static_cast<DWORD>(buffer.size()); |
+ if (!::ReadFile(file, &buffer[0], size, &size, NULL)) { |
+ DWORD error = GetLastError(); |
+ LOG_GETLASTERROR(ERROR) |
+ << "Failed to read '" << filename.value() << "'"; |
+ return HRESULT_FROM_WIN32(error); |
+ } |
+ |
+ // Parse the JSON configuration, expecting it to contain a dictionary. |
+ std::string file_content(&buffer[0], size); |
+ scoped_ptr<base::Value> value(base::JSONReader::Read(file_content, true)); |
+ |
+ base::DictionaryValue* dictionary; |
+ if (value.get() == NULL || !value->GetAsDictionary(&dictionary)) { |
+ LOG(ERROR) << "Failed to read '" << filename.value() << "'."; |
+ return E_FAIL; |
+ } |
+ |
+ value.release(); |
+ config_out->reset(dictionary); |
+ return S_OK; |
+} |
-using ATL::CComQIPtr; |
-using ATL::CComPtr; |
+} // namespace |
namespace remoting { |
@@ -22,52 +100,144 @@ HRESULT ElevatedControllerWin::FinalConstruct() { |
void ElevatedControllerWin::FinalRelease() { |
} |
-STDMETHODIMP ElevatedControllerWin::get_State(DaemonState* state_out) { |
- return E_NOTIMPL; |
-} |
+STDMETHODIMP ElevatedControllerWin::GetConfig(BSTR* config_out) { |
+ FilePath system_profile; |
+ PathService::Get(base::DIR_SYSTEM, &system_profile); |
+ |
+ // Read the host configuration. |
+ scoped_ptr<base::DictionaryValue> config; |
+ HRESULT hr = ReadConfig( |
+ system_profile.Append(kConfigDir).Append(kHostConfigFilename), |
+ &config); |
+ if (FAILED(hr)) { |
+ return hr; |
+ } |
-STDMETHODIMP ElevatedControllerWin::ReadConfig(BSTR* config_out) { |
- return E_NOTIMPL; |
+ // Build the filtered config. |
+ scoped_ptr<base::DictionaryValue> filtered_config( |
+ new base::DictionaryValue()); |
+ |
+ std::string value; |
+ if (config->GetString(kHostId, &value)) { |
+ filtered_config->SetString(kHostId, value); |
+ } |
+ if (config->GetString(kXmppLogin, &value)) { |
+ filtered_config->SetString(kXmppLogin, value); |
+ } |
+ |
+ // Convert the filtered config back to a string and return it to the caller. |
+ std::string file_content; |
+ base::JSONWriter::Write(filtered_config.get(), &file_content); |
+ |
+ *config_out = ::SysAllocString(UTF8ToUTF16(file_content).c_str()); |
+ if (config_out == NULL) { |
+ return E_OUTOFMEMORY; |
+ } |
+ |
+ return S_OK; |
} |
-STDMETHODIMP ElevatedControllerWin::WriteConfig(BSTR config) { |
- return E_NOTIMPL; |
+STDMETHODIMP ElevatedControllerWin::SetConfig(BSTR config) { |
+ // Determine the config directory path and create it if necessary. |
+ // N.B. The configuration files are stored in LocalSystems's profile which is |
+ // not readable by non administrators. |
+ FilePath system_profile; |
+ PathService::Get(base::DIR_SYSTEM, &system_profile); |
+ FilePath config_dir = system_profile.Append(kConfigDir); |
+ if (!file_util::CreateDirectory(config_dir)) { |
+ return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); |
+ } |
+ |
+ std::string file_content = UTF16ToUTF8( |
+ string16(static_cast<char16*>(config), ::SysStringLen(config))); |
+ |
+ int written = file_util::WriteFile( |
+ config_dir.Append(kAuthConfigFilename), |
+ file_content.c_str(), |
+ file_content.size()); |
+ if (written != static_cast<int>(file_content.size())) { |
+ return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); |
+ } |
+ |
+ // TODO(alexeypa): Store the authentication and host configurations in a |
+ // single file. |
+ written = file_util::WriteFile( |
+ config_dir.Append(kHostConfigFilename), |
+ file_content.c_str(), |
+ file_content.size()); |
+ if (written != static_cast<int>(file_content.size())) { |
+ return E_FAIL; |
+ } |
+ |
+ return S_OK; |
} |
STDMETHODIMP ElevatedControllerWin::StartDaemon() { |
- return E_NOTIMPL; |
-} |
+ ScopedScHandle service; |
+ HRESULT hr = OpenService(&service); |
+ if (FAILED(hr)) { |
+ return hr; |
+ } |
-STDMETHODIMP ElevatedControllerWin::StopDaemon() { |
- return E_NOTIMPL; |
+ if (!StartService(service, 0, NULL)) { |
+ DWORD error = GetLastError(); |
+ if (error != ERROR_SERVICE_ALREADY_RUNNING) { |
+ LOG_GETLASTERROR(ERROR) |
+ << "Failed to start the '" << kWindowsServiceName << "'service"; |
+ |
+ return HRESULT_FROM_WIN32(error); |
+ } |
+ } |
+ |
+ return S_OK; |
} |
-HRESULT ElevatedControllerWin::FireOnStateChange(DaemonState state) { |
- CComPtr<IConnectionPoint> connection_point; |
- FindConnectionPoint(__uuidof(IDaemonEvents), &connection_point); |
- if (!connection_point) { |
- return S_OK; |
+STDMETHODIMP ElevatedControllerWin::StopDaemon() { |
+ ScopedScHandle service; |
+ HRESULT hr = OpenService(&service); |
+ if (FAILED(hr)) { |
+ return hr; |
} |
- CComPtr<IEnumConnections> connections; |
- if (FAILED(connection_point->EnumConnections(&connections))) { |
- return S_OK; |
+ SERVICE_STATUS status; |
+ if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) { |
+ DWORD error = GetLastError(); |
+ if (error != ERROR_SERVICE_NOT_ACTIVE) { |
+ LOG_GETLASTERROR(ERROR) |
+ << "Failed to stop the '" << kWindowsServiceName << "'service"; |
+ return HRESULT_FROM_WIN32(error); |
+ } |
} |
- CONNECTDATA connect_data; |
- while (connections->Next(1, &connect_data, NULL) == S_OK) { |
- if (connect_data.pUnk != NULL) { |
- CComQIPtr<IDaemonEvents, &__uuidof(IDaemonEvents)> sink( |
- connect_data.pUnk); |
+ return S_OK; |
+} |
- if (sink != NULL) { |
- sink->OnStateChange(state); |
- } |
+HRESULT ElevatedControllerWin::OpenService(ScopedScHandle* service_out) { |
+ DWORD error; |
- connect_data.pUnk->Release(); |
- } |
+ ScopedScHandle scmanager( |
+ ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, |
+ SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); |
+ if (!scmanager.IsValid()) { |
+ error = GetLastError(); |
+ LOG_GETLASTERROR(ERROR) |
+ << "Failed to connect to the service control manager"; |
+ |
+ return HRESULT_FROM_WIN32(error); |
+ } |
+ |
+ ScopedScHandle service( |
+ ::OpenServiceW(scmanager, UTF8ToUTF16(kWindowsServiceName).c_str(), |
+ SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP)); |
+ if (!service.IsValid()) { |
+ error = GetLastError(); |
+ LOG_GETLASTERROR(ERROR) |
+ << "Failed to open to the '" << kWindowsServiceName << "' service"; |
+ |
+ return HRESULT_FROM_WIN32(error); |
} |
+ service_out->Set(service.Take()); |
return S_OK; |
} |