1// Copyright (c) 2012 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// A simple command-line app that registers and starts a host. 6 7#include <stdio.h> 8 9#include "base/at_exit.h" 10#include "base/command_line.h" 11#include "base/run_loop.h" 12#include "base/strings/stringprintf.h" 13#include "base/threading/thread.h" 14#include "net/url_request/url_fetcher.h" 15#include "net/url_request/url_request_context_getter.h" 16#include "remoting/base/service_urls.h" 17#include "remoting/base/url_request_context_getter.h" 18#include "remoting/host/setup/host_starter.h" 19#include "remoting/host/setup/oauth_helper.h" 20#include "remoting/host/setup/pin_validator.h" 21 22#if !defined(OS_WIN) 23#include <termios.h> 24#endif // !defined(OS_WIN) 25 26using remoting::HostStarter; 27 28// True if the host was started successfully. 29bool g_started = false; 30 31// The main message loop. 32base::MessageLoop* g_message_loop = NULL; 33 34// Lets us hide the PIN that a user types. 35void SetEcho(bool echo) { 36#if defined(OS_WIN) 37 DWORD mode; 38 HANDLE console_handle = GetStdHandle(STD_INPUT_HANDLE); 39 if (!GetConsoleMode(console_handle, &mode)) { 40 LOG(ERROR) << "GetConsoleMode failed"; 41 return; 42 } 43 SetConsoleMode(console_handle, 44 (mode & ~ENABLE_ECHO_INPUT) | (echo ? ENABLE_ECHO_INPUT : 0)); 45#else 46 termios term; 47 tcgetattr(STDIN_FILENO, &term); 48 if (echo) { 49 term.c_lflag |= ECHO; 50 } else { 51 term.c_lflag &= ~ECHO; 52 } 53 tcsetattr(STDIN_FILENO, TCSANOW, &term); 54#endif // !defined(OS_WIN) 55} 56 57// Reads a newline-terminated string from stdin. 58std::string ReadString(bool no_echo) { 59 if (no_echo) 60 SetEcho(false); 61 const int kMaxLen = 1024; 62 std::string str(kMaxLen, 0); 63 char* result = fgets(&str[0], kMaxLen, stdin); 64 if (no_echo) { 65 printf("\n"); 66 SetEcho(true); 67 } 68 if (!result) 69 return std::string(); 70 size_t newline_index = str.find('\n'); 71 if (newline_index != std::string::npos) 72 str[newline_index] = '\0'; 73 str.resize(strlen(&str[0])); 74 return str; 75} 76 77// Called when the HostStarter has finished. 78void OnDone(HostStarter::Result result) { 79 if (base::MessageLoop::current() != g_message_loop) { 80 g_message_loop->PostTask(FROM_HERE, base::Bind(&OnDone, result)); 81 return; 82 } 83 switch (result) { 84 case HostStarter::START_COMPLETE: 85 g_started = true; 86 break; 87 case HostStarter::NETWORK_ERROR: 88 fprintf(stderr, "Couldn't start host: network error.\n"); 89 break; 90 case HostStarter::OAUTH_ERROR: 91 fprintf(stderr, "Couldn't start host: OAuth error.\n"); 92 break; 93 case HostStarter::START_ERROR: 94 fprintf(stderr, "Couldn't start host.\n"); 95 break; 96 } 97 98 g_message_loop->QuitNow(); 99} 100 101int main(int argc, char** argv) { 102 // google_apis::GetOAuth2ClientID/Secret need a static CommandLine. 103 base::CommandLine::Init(argc, argv); 104 const base::CommandLine* command_line = 105 base::CommandLine::ForCurrentProcess(); 106 107 std::string host_name = command_line->GetSwitchValueASCII("name"); 108 std::string host_pin = command_line->GetSwitchValueASCII("pin"); 109 std::string auth_code = command_line->GetSwitchValueASCII("code"); 110 std::string redirect_url = command_line->GetSwitchValueASCII("redirect-url"); 111 112 if (host_name.empty()) { 113 fprintf(stderr, 114 "Usage: %s --name=<hostname> [--code=<auth-code>] [--pin=<PIN>] " 115 "[--redirect-url=<redirectURL>]\n", 116 argv[0]); 117 return 1; 118 } 119 120 if (host_pin.empty()) { 121 while (true) { 122 fprintf(stdout, "Enter a six-digit PIN: "); 123 fflush(stdout); 124 host_pin = ReadString(true); 125 if (!remoting::IsPinValid(host_pin)) { 126 fprintf(stdout, 127 "Please use a PIN consisting of at least six digits.\n"); 128 fflush(stdout); 129 continue; 130 } 131 std::string host_pin_confirm; 132 fprintf(stdout, "Enter the same PIN again: "); 133 fflush(stdout); 134 host_pin_confirm = ReadString(true); 135 if (host_pin != host_pin_confirm) { 136 fprintf(stdout, "You entered different PINs.\n"); 137 fflush(stdout); 138 continue; 139 } 140 break; 141 } 142 } else { 143 if (!remoting::IsPinValid(host_pin)) { 144 fprintf(stderr, "Please use a PIN consisting of at least six digits.\n"); 145 return 1; 146 } 147 } 148 149 if (auth_code.empty()) { 150 fprintf(stdout, "Enter an authorization code: "); 151 fflush(stdout); 152 auth_code = ReadString(true); 153 } 154 155 // This object instance is required by Chrome code (for example, 156 // FilePath, LazyInstance, MessageLoop). 157 base::AtExitManager exit_manager; 158 159 // Provide message loops and threads for the URLRequestContextGetter. 160 base::MessageLoop message_loop; 161 g_message_loop = &message_loop; 162 base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0); 163 base::Thread io_thread("IO thread"); 164 io_thread.StartWithOptions(io_thread_options); 165 base::Thread file_thread("file thread"); 166 file_thread.StartWithOptions(io_thread_options); 167 168 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter( 169 new remoting::URLRequestContextGetter(io_thread.task_runner(), 170 file_thread.task_runner())); 171 172 net::URLFetcher::SetIgnoreCertificateRequests(true); 173 174 // Start the host. 175 scoped_ptr<HostStarter> host_starter(HostStarter::Create( 176 remoting::ServiceUrls::GetInstance()->directory_hosts_url(), 177 url_request_context_getter.get())); 178 if (redirect_url.empty()) { 179 redirect_url = remoting::GetDefaultOauthRedirectUrl(); 180 } 181 host_starter->StartHost(host_name, host_pin, true, auth_code, redirect_url, 182 base::Bind(&OnDone)); 183 184 // Run the message loop until the StartHost completion callback. 185 base::RunLoop run_loop; 186 run_loop.Run(); 187 188 g_message_loop = NULL; 189 190 // Destroy the HostStarter and URLRequestContextGetter before stopping the 191 // IO thread. 192 host_starter.reset(); 193 url_request_context_getter = NULL; 194 195 io_thread.Stop(); 196 197 return g_started ? 0 : 1; 198} 199