123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "remoting/test/it2me_cli_host.h"
- #include "base/bind.h"
- #include "base/command_line.h"
- #include "base/json/json_reader.h"
- #include "base/json/json_writer.h"
- #include "base/run_loop.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "net/base/network_change_notifier.h"
- #include "remoting/base/auto_thread_task_runner.h"
- #include "remoting/base/logging.h"
- #include "remoting/host/chromoting_host_context.h"
- #include "remoting/host/it2me/it2me_constants.h"
- #include "remoting/host/it2me/it2me_native_messaging_host.h"
- #include "remoting/host/policy_watcher.h"
- #include "remoting/test/test_oauth_token_getter.h"
- #include "remoting/test/test_token_storage.h"
- namespace remoting {
- namespace {
- constexpr char kCRDDebugLog[] = "_debug_log";
- constexpr char kSwitchNameHelp[] = "help";
- constexpr char kSwitchNameUsername[] = "username";
- constexpr char kSwitchNameStoragePath[] = "storage-path";
- std::unique_ptr<It2MeNativeMessagingHost> CreateNativeMessagingHost(
- scoped_refptr<AutoThreadTaskRunner> ui_task_runner) {
- auto context = ChromotingHostContext::Create(ui_task_runner);
- std::unique_ptr<PolicyWatcher> policy_watcher =
- PolicyWatcher::CreateWithTaskRunner(context->file_task_runner(),
- context->management_service());
- auto factory = std::make_unique<It2MeHostFactory>();
- return std::make_unique<It2MeNativeMessagingHost>(
- /* needs_elevation */ false, std::move(policy_watcher),
- std::move(context), std::move(factory));
- }
- } // namespace
- // static
- bool It2MeCliHost::ShouldPrintHelp() {
- return base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchNameHelp);
- }
- // static
- void It2MeCliHost::PrintHelp() {
- fprintf(stderr,
- "Usage: %s [--storage-path=<storage-path>] "
- "[--username=<example@gmail.com>]\n ",
- base::CommandLine::ForCurrentProcess()
- ->GetProgram()
- .AsUTF8Unsafe()
- .c_str());
- }
- It2MeCliHost::It2MeCliHost() {}
- It2MeCliHost::~It2MeCliHost() = default;
- void It2MeCliHost::Start() {
- base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
- std::string username = cmd_line->GetSwitchValueASCII(kSwitchNameUsername);
- base::FilePath storage_path =
- cmd_line->GetSwitchValuePath(kSwitchNameStoragePath);
- storage_ = test::TestTokenStorage::OnDisk(username, storage_path);
- token_getter_ = std::make_unique<test::TestOAuthTokenGetter>(storage_.get());
- base::RunLoop initialize_token_getter_loop;
- token_getter_->Initialize(initialize_token_getter_loop.QuitClosure());
- initialize_token_getter_loop.Run();
- base::RunLoop ui_loop;
- ui_task_runner_ = new AutoThreadTaskRunner(
- base::ThreadTaskRunnerHandle::Get(), ui_loop.QuitClosure());
- token_getter_->CallWithToken(base::BindOnce(
- &It2MeCliHost::StartCRDHostAndGetCode, base::Unretained(this)));
- std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier(
- net::NetworkChangeNotifier::CreateIfNeeded());
- ui_loop.Run();
- }
- void It2MeCliHost::PostMessageFromNativeHost(const std::string& message) {
- auto message_value = base::JSONReader::Read(message);
- if (!message_value || !message_value->is_dict()) {
- OnProtocolBroken("Message is not a dictionary");
- return;
- }
- auto* type_value =
- message_value->FindKeyOfType(kMessageType, base::Value::Type::STRING);
- if (!type_value) {
- OnProtocolBroken("Message without type");
- return;
- }
- std::string type = type_value->GetString();
- if (type == kHelloResponse) {
- OnHelloResponse();
- } else if (type == kConnectResponse) {
- // Ok, just ignore.
- } else if (type == kDisconnectResponse) {
- OnDisconnectResponse();
- } else if (type == kHostStateChangedMessage) {
- // Handle CRD host state changes
- auto* state_value =
- message_value->FindKeyOfType(kState, base::Value::Type::STRING);
- if (!state_value) {
- OnProtocolBroken("No state in message");
- return;
- }
- std::string state = state_value->GetString();
- if (state == kHostStateReceivedAccessCode) {
- OnStateReceivedAccessCode(*message_value);
- } else if (state == kHostStateConnected) {
- OnStateRemoteConnected(*message_value);
- } else if (state == kHostStateDisconnected) {
- OnStateRemoteDisconnected();
- } else if (state == kHostStateError || state == kHostStateDomainError) {
- OnStateError(state, *message_value);
- } else if (state == kHostStateStarting ||
- state == kHostStateRequestedAccessCode) {
- // Just ignore these states.
- } else {
- LOG(WARNING) << "Unhandled state: " << state;
- }
- } else if (type == kCRDDebugLog) {
- // The It2Me host already prints the log to stdout/stderr.
- } else {
- LOG(WARNING) << "Unknown message type: " << type;
- }
- }
- void It2MeCliHost::CloseChannel(const std::string& error_message) {
- LOG(ERROR) << "CRD Host closed channel: " << error_message;
- command_awaiting_crd_access_code_ = false;
- ShutdownHost();
- }
- void It2MeCliHost::SendMessageToHost(const std::string& type,
- base::Value params) {
- std::string message_json;
- params.SetKey(kMessageType, base::Value(type));
- base::JSONWriter::Write(params, &message_json);
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&It2MeCliHost::DoSendMessage,
- weak_factory_.GetWeakPtr(), message_json));
- }
- void It2MeCliHost::DoSendMessage(const std::string& json) {
- if (!host_)
- return;
- host_->OnMessage(json);
- }
- void It2MeCliHost::OnProtocolBroken(const std::string& message) {
- LOG(ERROR) << "Error communicating with CRD Host : " << message;
- command_awaiting_crd_access_code_ = false;
- ShutdownHost();
- }
- void It2MeCliHost::StartCRDHostAndGetCode(OAuthTokenGetter::Status status,
- const std::string& user_email,
- const std::string& access_token) {
- DCHECK(!host_);
- // Store all parameters for future connect call.
- base::Value connect_params(base::Value::Type::DICTIONARY);
- connect_params.SetKey(kUserName, base::Value(user_email));
- connect_params.SetKey(kAuthServiceWithToken,
- base::Value("oauth2:" + access_token));
- connect_params.SetKey(kSuppressUserDialogs, base::Value(true));
- connect_params.SetKey(kSuppressNotifications, base::Value(true));
- connect_params_ = std::move(connect_params);
- remote_connected_ = false;
- command_awaiting_crd_access_code_ = true;
- host_ = CreateNativeMessagingHost(ui_task_runner_);
- host_->Start(this);
- base::Value params(base::Value::Type::DICTIONARY);
- SendMessageToHost(kHelloMessage, std::move(params));
- }
- void It2MeCliHost::ShutdownHost() {
- if (!host_)
- return;
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&It2MeCliHost::DoShutdownHost,
- weak_factory_.GetWeakPtr()));
- }
- void It2MeCliHost::DoShutdownHost() {
- host_.reset();
- ui_task_runner_.reset();
- }
- void It2MeCliHost::OnHelloResponse() {
- // Host is initialized, start connection.
- SendMessageToHost(kConnectMessage, std::move(connect_params_));
- }
- void It2MeCliHost::OnDisconnectResponse() {
- // Should happen only when remoting session finished and we
- // have requested host to shut down, or when we have got second auth code
- // without receiving connection.
- DCHECK(!command_awaiting_crd_access_code_);
- DCHECK(!remote_connected_);
- ShutdownHost();
- }
- void It2MeCliHost::OnStateError(const std::string& error_state,
- const base::Value& message) {
- std::string error_message;
- if (error_state == kHostStateDomainError) {
- error_message = "CRD Error : Invalid domain";
- } else {
- auto* error_code_value =
- message.FindKeyOfType(kErrorMessageCode, base::Value::Type::STRING);
- if (error_code_value)
- error_message = error_code_value->GetString();
- else
- error_message = "Unknown CRD Error";
- }
- // Notify callback if command is still running.
- if (command_awaiting_crd_access_code_) {
- command_awaiting_crd_access_code_ = false;
- LOG(ERROR) << "CRD Error state " + error_state;
- }
- // Shut down host, if any
- ShutdownHost();
- }
- void It2MeCliHost::OnStateRemoteConnected(const base::Value& message) {
- remote_connected_ = true;
- auto* client_value =
- message.FindKeyOfType(kClient, base::Value::Type::STRING);
- if (client_value) {
- HOST_LOG << "Remote connection by " << client_value->GetString();
- }
- }
- void It2MeCliHost::OnStateRemoteDisconnected() {
- // There could be a connection attempt that was not successful, we will
- // receive "disconnected" message without actually receiving "connected".
- if (!remote_connected_)
- return;
- remote_connected_ = false;
- // Remote has disconnected, time to send "disconnect" that would result
- // in shutting down the host.
- base::Value params(base::Value::Type::DICTIONARY);
- SendMessageToHost(kDisconnectMessage, std::move(params));
- }
- void It2MeCliHost::OnStateReceivedAccessCode(const base::Value& message) {
- if (!command_awaiting_crd_access_code_) {
- if (!remote_connected_) {
- // We have already sent the access code back to the server which initiated
- // this CRD session through a remote command, and we can not send a new
- // access code. Assuming that the old access code is no longer valid, we
- // can only terminate the current CRD session.
- base::Value params(base::Value::Type::DICTIONARY);
- SendMessageToHost(kDisconnectMessage, std::move(params));
- }
- return;
- }
- auto* code_value =
- message.FindKeyOfType(kAccessCode, base::Value::Type::STRING);
- auto* code_lifetime_value =
- message.FindKeyOfType(kAccessCodeLifetime, base::Value::Type::INTEGER);
- if (!code_value || !code_lifetime_value) {
- OnProtocolBroken("Can not obtain access code");
- return;
- }
- command_awaiting_crd_access_code_ = false;
- // Prints the access code.
- base::TimeDelta expires_in = base::Seconds(code_lifetime_value->GetInt());
- HOST_LOG << "It2Me access code is generated: " << code_value->GetString();
- HOST_LOG << "Expires at: " << (base::Time::Now() + expires_in);
- }
- } // namespace remoting
|