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