| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "remoting/host/setup/native_messaging_host.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/basictypes.h" | |
| 10 #include "base/bind.h" | |
| 11 #include "base/callback.h" | |
| 12 #include "base/command_line.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/message_loop/message_loop.h" | |
| 15 #include "base/run_loop.h" | |
| 16 #include "base/strings/string_number_conversions.h" | |
| 17 #include "base/strings/stringize_macros.h" | |
| 18 #include "base/threading/thread.h" | |
| 19 #include "base/values.h" | |
| 20 #include "google_apis/gaia/gaia_oauth_client.h" | |
| 21 #include "google_apis/google_api_keys.h" | |
| 22 #include "net/base/net_util.h" | |
| 23 #include "net/url_request/url_fetcher.h" | |
| 24 #include "remoting/base/rsa_key_pair.h" | |
| 25 #include "remoting/base/url_request_context.h" | |
| 26 #include "remoting/host/host_exit_codes.h" | |
| 27 #include "remoting/host/pairing_registry_delegate.h" | |
| 28 #include "remoting/host/pin_hash.h" | |
| 29 #include "remoting/host/setup/oauth_client.h" | |
| 30 #include "remoting/protocol/pairing_registry.h" | |
| 31 | |
| 32 namespace { | |
| 33 | |
| 34 const char kParentWindowSwitchName[] = "parent-window"; | |
| 35 | |
| 36 // redirect_uri to use when authenticating service accounts (service account | |
| 37 // codes are obtained "out-of-band", i.e., not through an OAuth redirect). | |
| 38 const char* kServiceAccountRedirectUri = "oob"; | |
| 39 | |
| 40 // Features supported in addition to the base protocol. | |
| 41 const char* kSupportedFeatures[] = { | |
| 42 "pairingRegistry", | |
| 43 "oauthClient" | |
| 44 }; | |
| 45 | |
| 46 // Helper to extract the "config" part of a message as a DictionaryValue. | |
| 47 // Returns NULL on failure, and logs an error message. | |
| 48 scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage( | |
| 49 const base::DictionaryValue& message) { | |
| 50 scoped_ptr<base::DictionaryValue> result; | |
| 51 const base::DictionaryValue* config_dict; | |
| 52 if (message.GetDictionary("config", &config_dict)) { | |
| 53 result.reset(config_dict->DeepCopy()); | |
| 54 } else { | |
| 55 LOG(ERROR) << "'config' dictionary not found"; | |
| 56 } | |
| 57 return result.Pass(); | |
| 58 } | |
| 59 | |
| 60 } // namespace | |
| 61 | |
| 62 namespace remoting { | |
| 63 | |
| 64 NativeMessagingHost::NativeMessagingHost( | |
| 65 scoped_refptr<DaemonController> daemon_controller, | |
| 66 scoped_refptr<protocol::PairingRegistry> pairing_registry, | |
| 67 scoped_ptr<OAuthClient> oauth_client) | |
| 68 : daemon_controller_(daemon_controller), | |
| 69 pairing_registry_(pairing_registry), | |
| 70 oauth_client_(oauth_client.Pass()), | |
| 71 weak_factory_(this) { | |
| 72 weak_ptr_ = weak_factory_.GetWeakPtr(); | |
| 73 } | |
| 74 | |
| 75 NativeMessagingHost::~NativeMessagingHost() { | |
| 76 } | |
| 77 | |
| 78 void NativeMessagingHost::ProcessMessage( | |
| 79 scoped_ptr<base::DictionaryValue> message, | |
| 80 const SendResponseCallback& done) { | |
| 81 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue()); | |
| 82 | |
| 83 // If the client supplies an ID, it will expect it in the response. This | |
| 84 // might be a string or a number, so cope with both. | |
| 85 const base::Value* id; | |
| 86 if (message->Get("id", &id)) | |
| 87 response->Set("id", id->DeepCopy()); | |
| 88 | |
| 89 std::string type; | |
| 90 if (!message->GetString("type", &type)) { | |
| 91 LOG(ERROR) << "'type' not found"; | |
| 92 done.Run(scoped_ptr<base::DictionaryValue>()); | |
| 93 return; | |
| 94 } | |
| 95 | |
| 96 response->SetString("type", type + "Response"); | |
| 97 | |
| 98 bool success = false; | |
| 99 if (type == "hello") { | |
| 100 success = ProcessHello(*message, response.Pass(), done); | |
| 101 } else if (type == "clearPairedClients") { | |
| 102 success = ProcessClearPairedClients(*message, response.Pass(), done); | |
| 103 } else if (type == "deletePairedClient") { | |
| 104 success = ProcessDeletePairedClient(*message, response.Pass(), done); | |
| 105 } else if (type == "getHostName") { | |
| 106 success = ProcessGetHostName(*message, response.Pass(), done); | |
| 107 } else if (type == "getPinHash") { | |
| 108 success = ProcessGetPinHash(*message, response.Pass(), done); | |
| 109 } else if (type == "generateKeyPair") { | |
| 110 success = ProcessGenerateKeyPair(*message, response.Pass(), done); | |
| 111 } else if (type == "updateDaemonConfig") { | |
| 112 success = ProcessUpdateDaemonConfig(*message, response.Pass(), done); | |
| 113 } else if (type == "getDaemonConfig") { | |
| 114 success = ProcessGetDaemonConfig(*message, response.Pass(), done); | |
| 115 } else if (type == "getPairedClients") { | |
| 116 success = ProcessGetPairedClients(*message, response.Pass(), done); | |
| 117 } else if (type == "getUsageStatsConsent") { | |
| 118 success = ProcessGetUsageStatsConsent(*message, response.Pass(), done); | |
| 119 } else if (type == "startDaemon") { | |
| 120 success = ProcessStartDaemon(*message, response.Pass(), done); | |
| 121 } else if (type == "stopDaemon") { | |
| 122 success = ProcessStopDaemon(*message, response.Pass(), done); | |
| 123 } else if (type == "getDaemonState") { | |
| 124 success = ProcessGetDaemonState(*message, response.Pass(), done); | |
| 125 } else if (type == "getHostClientId") { | |
| 126 success = ProcessGetHostClientId(*message, response.Pass(), done); | |
| 127 } else if (type == "getCredentialsFromAuthCode") { | |
| 128 success = ProcessGetCredentialsFromAuthCode(*message, response.Pass(), | |
| 129 done); | |
| 130 } else { | |
| 131 LOG(ERROR) << "Unsupported request type: " << type; | |
| 132 } | |
| 133 | |
| 134 if (!success) | |
| 135 done.Run(scoped_ptr<base::DictionaryValue>()); | |
| 136 } | |
| 137 | |
| 138 bool NativeMessagingHost::ProcessHello( | |
| 139 const base::DictionaryValue& message, | |
| 140 scoped_ptr<base::DictionaryValue> response, | |
| 141 const SendResponseCallback& done) { | |
| 142 response->SetString("version", STRINGIZE(VERSION)); | |
| 143 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue()); | |
| 144 supported_features_list->AppendStrings(std::vector<std::string>( | |
| 145 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures))); | |
| 146 response->Set("supportedFeatures", supported_features_list.release()); | |
| 147 done.Run(response.Pass()); | |
| 148 return true; | |
| 149 } | |
| 150 | |
| 151 bool NativeMessagingHost::ProcessClearPairedClients( | |
| 152 const base::DictionaryValue& message, | |
| 153 scoped_ptr<base::DictionaryValue> response, | |
| 154 const SendResponseCallback& done) { | |
| 155 if (pairing_registry_) { | |
| 156 pairing_registry_->ClearAllPairings( | |
| 157 base::Bind(&NativeMessagingHost::SendBooleanResult, weak_ptr_, | |
| 158 done, base::Passed(&response))); | |
| 159 } else { | |
| 160 SendBooleanResult(done, response.Pass(), false); | |
| 161 } | |
| 162 return true; | |
| 163 } | |
| 164 | |
| 165 bool NativeMessagingHost::ProcessDeletePairedClient( | |
| 166 const base::DictionaryValue& message, | |
| 167 scoped_ptr<base::DictionaryValue> response, | |
| 168 const SendResponseCallback& done) { | |
| 169 std::string client_id; | |
| 170 if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) { | |
| 171 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey | |
| 172 << "' string not found."; | |
| 173 return false; | |
| 174 } | |
| 175 | |
| 176 if (pairing_registry_) { | |
| 177 pairing_registry_->DeletePairing( | |
| 178 client_id, base::Bind(&NativeMessagingHost::SendBooleanResult, | |
| 179 weak_ptr_, done, base::Passed(&response))); | |
| 180 } else { | |
| 181 SendBooleanResult(done, response.Pass(), false); | |
| 182 } | |
| 183 return true; | |
| 184 } | |
| 185 | |
| 186 bool NativeMessagingHost::ProcessGetHostName( | |
| 187 const base::DictionaryValue& message, | |
| 188 scoped_ptr<base::DictionaryValue> response, | |
| 189 const SendResponseCallback& done) { | |
| 190 response->SetString("hostname", net::GetHostName()); | |
| 191 done.Run(response.Pass()); | |
| 192 return true; | |
| 193 } | |
| 194 | |
| 195 bool NativeMessagingHost::ProcessGetPinHash( | |
| 196 const base::DictionaryValue& message, | |
| 197 scoped_ptr<base::DictionaryValue> response, | |
| 198 const SendResponseCallback& done) { | |
| 199 std::string host_id; | |
| 200 if (!message.GetString("hostId", &host_id)) { | |
| 201 LOG(ERROR) << "'hostId' not found: " << message; | |
| 202 return false; | |
| 203 } | |
| 204 std::string pin; | |
| 205 if (!message.GetString("pin", &pin)) { | |
| 206 LOG(ERROR) << "'pin' not found: " << message; | |
| 207 return false; | |
| 208 } | |
| 209 response->SetString("hash", MakeHostPinHash(host_id, pin)); | |
| 210 done.Run(response.Pass()); | |
| 211 return true; | |
| 212 } | |
| 213 | |
| 214 bool NativeMessagingHost::ProcessGenerateKeyPair( | |
| 215 const base::DictionaryValue& message, | |
| 216 scoped_ptr<base::DictionaryValue> response, | |
| 217 const SendResponseCallback& done) { | |
| 218 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate(); | |
| 219 response->SetString("privateKey", key_pair->ToString()); | |
| 220 response->SetString("publicKey", key_pair->GetPublicKey()); | |
| 221 done.Run(response.Pass()); | |
| 222 return true; | |
| 223 } | |
| 224 | |
| 225 bool NativeMessagingHost::ProcessUpdateDaemonConfig( | |
| 226 const base::DictionaryValue& message, | |
| 227 scoped_ptr<base::DictionaryValue> response, | |
| 228 const SendResponseCallback& done) { | |
| 229 scoped_ptr<base::DictionaryValue> config_dict = | |
| 230 ConfigDictionaryFromMessage(message); | |
| 231 if (!config_dict) | |
| 232 return false; | |
| 233 | |
| 234 daemon_controller_->UpdateConfig( | |
| 235 config_dict.Pass(), | |
| 236 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, | |
| 237 done, base::Passed(&response))); | |
| 238 return true; | |
| 239 } | |
| 240 | |
| 241 bool NativeMessagingHost::ProcessGetDaemonConfig( | |
| 242 const base::DictionaryValue& message, | |
| 243 scoped_ptr<base::DictionaryValue> response, | |
| 244 const SendResponseCallback& done) { | |
| 245 daemon_controller_->GetConfig( | |
| 246 base::Bind(&NativeMessagingHost::SendConfigResponse, weak_ptr_, | |
| 247 done, base::Passed(&response))); | |
| 248 return true; | |
| 249 } | |
| 250 | |
| 251 bool NativeMessagingHost::ProcessGetPairedClients( | |
| 252 const base::DictionaryValue& message, | |
| 253 scoped_ptr<base::DictionaryValue> response, | |
| 254 const SendResponseCallback& done) { | |
| 255 if (pairing_registry_) { | |
| 256 pairing_registry_->GetAllPairings( | |
| 257 base::Bind(&NativeMessagingHost::SendPairedClientsResponse, weak_ptr_, | |
| 258 done, base::Passed(&response))); | |
| 259 } else { | |
| 260 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue); | |
| 261 SendPairedClientsResponse(done, response.Pass(), no_paired_clients.Pass()); | |
| 262 } | |
| 263 return true; | |
| 264 } | |
| 265 | |
| 266 bool NativeMessagingHost::ProcessGetUsageStatsConsent( | |
| 267 const base::DictionaryValue& message, | |
| 268 scoped_ptr<base::DictionaryValue> response, | |
| 269 const SendResponseCallback& done) { | |
| 270 daemon_controller_->GetUsageStatsConsent( | |
| 271 base::Bind(&NativeMessagingHost::SendUsageStatsConsentResponse, | |
| 272 weak_ptr_, done, base::Passed(&response))); | |
| 273 return true; | |
| 274 } | |
| 275 | |
| 276 bool NativeMessagingHost::ProcessStartDaemon( | |
| 277 const base::DictionaryValue& message, | |
| 278 scoped_ptr<base::DictionaryValue> response, | |
| 279 const SendResponseCallback& done) { | |
| 280 bool consent; | |
| 281 if (!message.GetBoolean("consent", &consent)) { | |
| 282 LOG(ERROR) << "'consent' not found."; | |
| 283 return false; | |
| 284 } | |
| 285 | |
| 286 scoped_ptr<base::DictionaryValue> config_dict = | |
| 287 ConfigDictionaryFromMessage(message); | |
| 288 if (!config_dict) | |
| 289 return false; | |
| 290 | |
| 291 daemon_controller_->SetConfigAndStart( | |
| 292 config_dict.Pass(), consent, | |
| 293 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, | |
| 294 done, base::Passed(&response))); | |
| 295 return true; | |
| 296 } | |
| 297 | |
| 298 bool NativeMessagingHost::ProcessStopDaemon( | |
| 299 const base::DictionaryValue& message, | |
| 300 scoped_ptr<base::DictionaryValue> response, | |
| 301 const SendResponseCallback& done) { | |
| 302 daemon_controller_->Stop( | |
| 303 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, | |
| 304 done, base::Passed(&response))); | |
| 305 return true; | |
| 306 } | |
| 307 | |
| 308 bool NativeMessagingHost::ProcessGetDaemonState( | |
| 309 const base::DictionaryValue& message, | |
| 310 scoped_ptr<base::DictionaryValue> response, | |
| 311 const SendResponseCallback& done) { | |
| 312 DaemonController::State state = daemon_controller_->GetState(); | |
| 313 switch (state) { | |
| 314 case DaemonController::STATE_NOT_IMPLEMENTED: | |
| 315 response->SetString("state", "NOT_IMPLEMENTED"); | |
| 316 break; | |
| 317 case DaemonController::STATE_NOT_INSTALLED: | |
| 318 response->SetString("state", "NOT_INSTALLED"); | |
| 319 break; | |
| 320 case DaemonController::STATE_INSTALLING: | |
| 321 response->SetString("state", "INSTALLING"); | |
| 322 break; | |
| 323 case DaemonController::STATE_STOPPED: | |
| 324 response->SetString("state", "STOPPED"); | |
| 325 break; | |
| 326 case DaemonController::STATE_STARTING: | |
| 327 response->SetString("state", "STARTING"); | |
| 328 break; | |
| 329 case DaemonController::STATE_STARTED: | |
| 330 response->SetString("state", "STARTED"); | |
| 331 break; | |
| 332 case DaemonController::STATE_STOPPING: | |
| 333 response->SetString("state", "STOPPING"); | |
| 334 break; | |
| 335 case DaemonController::STATE_UNKNOWN: | |
| 336 response->SetString("state", "UNKNOWN"); | |
| 337 break; | |
| 338 } | |
| 339 done.Run(response.Pass()); | |
| 340 return true; | |
| 341 } | |
| 342 | |
| 343 bool NativeMessagingHost::ProcessGetHostClientId( | |
| 344 const base::DictionaryValue& message, | |
| 345 scoped_ptr<base::DictionaryValue> response, | |
| 346 const SendResponseCallback& done) { | |
| 347 response->SetString("clientId", google_apis::GetOAuth2ClientID( | |
| 348 google_apis::CLIENT_REMOTING_HOST)); | |
| 349 done.Run(response.Pass()); | |
| 350 return true; | |
| 351 } | |
| 352 | |
| 353 bool NativeMessagingHost::ProcessGetCredentialsFromAuthCode( | |
| 354 const base::DictionaryValue& message, | |
| 355 scoped_ptr<base::DictionaryValue> response, | |
| 356 const SendResponseCallback& done) { | |
| 357 std::string auth_code; | |
| 358 if (!message.GetString("authorizationCode", &auth_code)) { | |
| 359 LOG(ERROR) << "'authorizationCode' string not found."; | |
| 360 return false; | |
| 361 } | |
| 362 | |
| 363 gaia::OAuthClientInfo oauth_client_info = { | |
| 364 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST), | |
| 365 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST), | |
| 366 kServiceAccountRedirectUri | |
| 367 }; | |
| 368 | |
| 369 oauth_client_->GetCredentialsFromAuthCode( | |
| 370 oauth_client_info, auth_code, base::Bind( | |
| 371 &NativeMessagingHost::SendCredentialsResponse, weak_ptr_, | |
| 372 done, base::Passed(&response))); | |
| 373 | |
| 374 return true; | |
| 375 } | |
| 376 | |
| 377 void NativeMessagingHost::SendConfigResponse( | |
| 378 const SendResponseCallback& done, | |
| 379 scoped_ptr<base::DictionaryValue> response, | |
| 380 scoped_ptr<base::DictionaryValue> config) { | |
| 381 if (config) { | |
| 382 response->Set("config", config.release()); | |
| 383 } else { | |
| 384 response->Set("config", Value::CreateNullValue()); | |
| 385 } | |
| 386 done.Run(response.Pass()); | |
| 387 } | |
| 388 | |
| 389 void NativeMessagingHost::SendPairedClientsResponse( | |
| 390 const SendResponseCallback& done, | |
| 391 scoped_ptr<base::DictionaryValue> response, | |
| 392 scoped_ptr<base::ListValue> pairings) { | |
| 393 response->Set("pairedClients", pairings.release()); | |
| 394 done.Run(response.Pass()); | |
| 395 } | |
| 396 | |
| 397 void NativeMessagingHost::SendUsageStatsConsentResponse( | |
| 398 const SendResponseCallback& done, | |
| 399 scoped_ptr<base::DictionaryValue> response, | |
| 400 const DaemonController::UsageStatsConsent& consent) { | |
| 401 response->SetBoolean("supported", consent.supported); | |
| 402 response->SetBoolean("allowed", consent.allowed); | |
| 403 response->SetBoolean("setByPolicy", consent.set_by_policy); | |
| 404 done.Run(response.Pass()); | |
| 405 } | |
| 406 | |
| 407 void NativeMessagingHost::SendAsyncResult( | |
| 408 const SendResponseCallback& done, | |
| 409 scoped_ptr<base::DictionaryValue> response, | |
| 410 DaemonController::AsyncResult result) { | |
| 411 switch (result) { | |
| 412 case DaemonController::RESULT_OK: | |
| 413 response->SetString("result", "OK"); | |
| 414 break; | |
| 415 case DaemonController::RESULT_FAILED: | |
| 416 response->SetString("result", "FAILED"); | |
| 417 break; | |
| 418 case DaemonController::RESULT_CANCELLED: | |
| 419 response->SetString("result", "CANCELLED"); | |
| 420 break; | |
| 421 case DaemonController::RESULT_FAILED_DIRECTORY: | |
| 422 response->SetString("result", "FAILED_DIRECTORY"); | |
| 423 break; | |
| 424 } | |
| 425 done.Run(response.Pass()); | |
| 426 } | |
| 427 | |
| 428 void NativeMessagingHost::SendBooleanResult( | |
| 429 const SendResponseCallback& done, | |
| 430 scoped_ptr<base::DictionaryValue> response, bool result) { | |
| 431 response->SetBoolean("result", result); | |
| 432 done.Run(response.Pass()); | |
| 433 } | |
| 434 | |
| 435 void NativeMessagingHost::SendCredentialsResponse( | |
| 436 const SendResponseCallback& done, | |
| 437 scoped_ptr<base::DictionaryValue> response, | |
| 438 const std::string& user_email, | |
| 439 const std::string& refresh_token) { | |
| 440 response->SetString("userEmail", user_email); | |
| 441 response->SetString("refreshToken", refresh_token); | |
| 442 done.Run(response.Pass()); | |
| 443 } | |
| 444 | |
| 445 int NativeMessagingHostMain() { | |
| 446 #if defined(OS_WIN) | |
| 447 // GetStdHandle() returns pseudo-handles for stdin and stdout even if | |
| 448 // the hosting executable specifies "Windows" subsystem. However the returned | |
| 449 // handles are invalid in that case unless standard input and output are | |
| 450 // redirected to a pipe or file. | |
| 451 base::PlatformFile read_file = GetStdHandle(STD_INPUT_HANDLE); | |
| 452 base::PlatformFile write_file = GetStdHandle(STD_OUTPUT_HANDLE); | |
| 453 #elif defined(OS_POSIX) | |
| 454 base::PlatformFile read_file = STDIN_FILENO; | |
| 455 base::PlatformFile write_file = STDOUT_FILENO; | |
| 456 #else | |
| 457 #error Not implemented. | |
| 458 #endif | |
| 459 | |
| 460 // Mac OS X requires that the main thread be a UI message loop in order to | |
| 461 // receive distributed notifications from the System Preferences pane. An | |
| 462 // IO thread is needed for the pairing registry and URL context getter. | |
| 463 base::Thread io_thread("io_thread"); | |
| 464 io_thread.StartWithOptions( | |
| 465 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
| 466 | |
| 467 base::MessageLoopForUI message_loop; | |
| 468 base::RunLoop run_loop; | |
| 469 | |
| 470 scoped_refptr<DaemonController> daemon_controller = | |
| 471 DaemonController::Create(); | |
| 472 | |
| 473 // Pass handle of the native view to the controller so that the UAC prompts | |
| 474 // are focused properly. | |
| 475 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
| 476 if (command_line->HasSwitch(kParentWindowSwitchName)) { | |
| 477 std::string native_view = | |
| 478 command_line->GetSwitchValueASCII(kParentWindowSwitchName); | |
| 479 int64 native_view_handle = 0; | |
| 480 if (base::StringToInt64(native_view, &native_view_handle)) { | |
| 481 daemon_controller->SetWindow(reinterpret_cast<void*>(native_view_handle)); | |
| 482 } else { | |
| 483 LOG(WARNING) << "Invalid parameter value --" << kParentWindowSwitchName | |
| 484 << "=" << native_view; | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 // OAuth client (for credential requests). | |
| 489 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter( | |
| 490 new URLRequestContextGetter(io_thread.message_loop_proxy())); | |
| 491 scoped_ptr<OAuthClient> oauth_client( | |
| 492 new OAuthClient(url_request_context_getter)); | |
| 493 | |
| 494 net::URLFetcher::SetIgnoreCertificateRequests(true); | |
| 495 | |
| 496 // Create the pairing registry and native messaging host. | |
| 497 scoped_refptr<protocol::PairingRegistry> pairing_registry = | |
| 498 CreatePairingRegistry(io_thread.message_loop_proxy()); | |
| 499 scoped_ptr<NativeMessagingChannel::Delegate> host( | |
| 500 new NativeMessagingHost(daemon_controller, | |
| 501 pairing_registry, | |
| 502 oauth_client.Pass())); | |
| 503 | |
| 504 // Set up the native messaging channel. | |
| 505 scoped_ptr<NativeMessagingChannel> channel( | |
| 506 new NativeMessagingChannel(host.Pass(), read_file, write_file)); | |
| 507 channel->Start(run_loop.QuitClosure()); | |
| 508 | |
| 509 // Run the loop until channel is alive. | |
| 510 run_loop.Run(); | |
| 511 return kSuccessExitCode; | |
| 512 } | |
| 513 | |
| 514 } // namespace remoting | |
| OLD | NEW |