Chromium Code Reviews| 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..aad59ba7ec7e83050ee6174119e970658e63e2b0 100644 |
| --- a/remoting/host/elevated_controller_win.cc |
| +++ b/remoting/host/elevated_controller_win.cc |
| @@ -4,11 +4,87 @@ |
| #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 { |
| + |
| +// TODO(alexeypa): remove this limitation. |
| +const size_t kMaxConfigFileSize = 0x10000; |
|
Wez
2012/03/30 22:11:01
nit: Specifying the max file size in hex makes it
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + |
| +const char kHostId[] = "host_id"; |
| +const char kXmppLogin[] = "xmpp_login"; |
|
Wez
2012/03/30 22:11:01
nit: Add a comment to explain what these constants
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + |
| +// Names of the configuration files. |
| +const FilePath::CharType kAuthConfig[] = FILE_PATH_LITERAL("auth.json"); |
|
Wez
2012/03/30 22:11:01
nit: These should really be kAuthConfig -> kAuthCo
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| +const FilePath::CharType kHostConfig[] = FILE_PATH_LITERAL("host.json"); |
| + |
| +// TODO(alexeypa): remove the hardcoded undocimented paths and store |
|
Wez
2012/03/30 22:11:01
typos: remove -> Remove, undocimented.
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| +// 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 (up to 64KB in size). |
|
Wez
2012/03/30 22:11:01
nit: The "up to 64KB in size" comment goes with th
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| +HRESULT ReadConfig(const FilePath& filename, |
| + scoped_ptr<base::DictionaryValue>* config_out) { |
| + // 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); |
| + } |
| -using ATL::CComQIPtr; |
| -using ATL::CComPtr; |
| + // Parse JSON data. |
|
Wez
2012/03/30 22:11:01
nit: "Parse the JSON configuration, expecting it t
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + 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); |
|
Wez
2012/03/30 22:11:01
Gah. Gross. Can't see a better way to do it, off
|
| + return S_OK; |
| +} |
| + |
| +} // namespace |
| namespace remoting { |
| @@ -22,52 +98,143 @@ 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); |
| + |
| + // Build the host configuration. |
|
Wez
2012/03/30 22:11:01
nit: Isn't this reading it, rather than "building"
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + scoped_ptr<base::DictionaryValue> config; |
| + HRESULT hr = ReadConfig(system_profile.Append(kConfigDir).Append(kHostConfig), |
| + &config); |
| + if (FAILED(hr)) { |
| + return hr; |
| + } |
| -STDMETHODIMP ElevatedControllerWin::ReadConfig(BSTR* config_out) { |
| - return E_NOTIMPL; |
| + // Build the filtered config. |
|
Wez
2012/03/30 22:11:01
Why? What is the purpose of the filtering?
alexeypa (please no reviews)
2012/03/30 23:47:09
This matched Mac implementation.
Lambros
2012/03/31 00:48:34
GetConfig() is documented to return only certain k
Wez
2012/04/01 01:25:27
Right; my point is that the comment should clarify
alexeypa (please no reviews)
2012/04/02 16:26:39
Oh, well. I'll try to address this in one of the f
|
| + scoped_ptr<base::DictionaryValue> filtered_config( |
| + new base::DictionaryValue()); |
| + |
| + std::string value; |
| + if (config->GetString(kHostId, &value)) { |
| + filtered_config->SetString(kHostId, value); |
| + } |
| + |
|
Wez
2012/03/30 22:11:01
nit: No need for this blank line.
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + if (config->GetString(kXmppLogin, &value)) { |
| + filtered_config->SetString(kXmppLogin, value); |
| + } |
| + |
| + // Convert the filtered config back to string and return it to the caller. |
|
Wez
2012/03/30 22:11:01
typo: ... back to a string ...
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + 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) { |
| + FilePath system_profile; |
|
Wez
2012/03/30 22:11:01
nit: Comment before these lines e.g. "Determine th
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + PathService::Get(base::DIR_SYSTEM, &system_profile); |
| + |
|
Wez
2012/03/30 22:11:01
nit: No need for this blank line?
alexeypa (please no reviews)
2012/03/30 23:47:09
Removed. My eyes hurt now! :-)
|
| + 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( |
|
Wez
2012/03/30 22:11:01
Should we be doing the write-temp-file-then-delete
alexeypa (please no reviews)
2012/03/30 23:47:09
We could. However the whole configuration flow is
|
| + config_dir.Append(kAuthConfig), |
| + file_content.c_str(), |
| + file_content.size()); |
| + |
| + if (written != static_cast<int>(file_content.size())) { |
| + return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); |
| + } |
| + |
| + // TODO(alexeypa): make it a single file. |
|
Wez
2012/03/30 22:11:01
nit: I think you mean "Store the authentication an
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + written = file_util::WriteFile( |
| + config_dir.Append(kHostConfig), |
| + file_content.c_str(), |
| + file_content.size()); |
| + |
| + if (written != static_cast<int>(file_content.size())) { |
| + return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); |
|
Wez
2012/03/30 22:11:01
nit: That's not strictly the right error, and in f
alexeypa (please no reviews)
2012/03/30 23:47:09
Nope. I bet something else bad happened. I replace
|
| + } |
| + |
| + 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; |
|
Wez
2012/03/30 22:11:01
Do you actually want to return potentially arbitra
alexeypa (please no reviews)
2012/03/30 23:47:09
I don't think so. Something better defined is "ope
Wez
2012/04/01 01:25:27
The word "here" was missing from my sentence; I th
alexeypa (please no reviews)
2012/04/02 16:26:39
That is not the only reason. I don't think that us
|
| } |
| - 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; |
| +} |
| + |
| +HRESULT ElevatedControllerWin::OpenService(ScopedScHandle* service_out) { |
| + DWORD error; |
| - if (sink != NULL) { |
| - sink->OnStateChange(state); |
| - } |
| + 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"; |
| - connect_data.pUnk->Release(); |
| - } |
| + return HRESULT_FROM_WIN32(error); |
| + } |
| + |
| + ScopedScHandle service( |
| + ::OpenServiceA(scmanager, kWindowsServiceName, |
|
Wez
2012/03/30 22:11:01
nit: Why are we using OpenServiceA but OpenSCManag
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
|
| + 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; |
| } |