Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / remoting / webapp / host_controller.js
blob2a613fdc201c39fc05bb882fcff1c4d6ee295c4e
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.
5 'use strict';
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
10 /** @constructor */
11 remoting.HostController = function() {
12 /** @type {remoting.HostController} */
13 var that = this;
15 /** @type {boolean} @private */
16 this.pluginSupported_ = true;
18 /** @type {remoting.HostNativeMessaging} @private */
19 this.plugin_ = new remoting.HostNativeMessaging();
21 /** @param {boolean} success */
22 var onNativeMessagingInit = function(success) {
23 if (success) {
24 console.log('Native Messaging supported.');
25 } else {
26 console.log('Native Messaging unsupported, falling back to NPAPI.');
27 var plugin = remoting.HostSession.createPlugin();
28 that.plugin_ = new remoting.HostPluginWrapper(plugin);
29 /** @type {HTMLElement} @private */
30 var container = document.getElementById('daemon-plugin-container');
31 container.appendChild(plugin);
34 /** @param {string} version */
35 var printVersion = function(version) {
36 if (version == '') {
37 console.log('Host not installed.');
38 } else {
39 console.log('Host version: ' + version);
42 that.pluginSupported_ = true;
43 try {
44 that.plugin_.getDaemonVersion(printVersion);
45 } catch (err) {
46 console.log('Host version not available.');
47 that.pluginSupported_ = false;
51 this.plugin_.initialize(onNativeMessagingInit);
54 // Note that the values in the enums below are copied from
55 // daemon_controller.h and must be kept in sync.
56 /** @enum {number} */
57 remoting.HostController.State = {
58 NOT_IMPLEMENTED: -1,
59 NOT_INSTALLED: 0,
60 INSTALLING: 1,
61 STOPPED: 2,
62 STARTING: 3,
63 STARTED: 4,
64 STOPPING: 5,
65 UNKNOWN: 6
68 /** @enum {number} */
69 remoting.HostController.AsyncResult = {
70 OK: 0,
71 FAILED: 1,
72 CANCELLED: 2,
73 FAILED_DIRECTORY: 3
76 /**
77 * Checks whether or not the host plugin is valid.
79 * @return {boolean} True if the plugin is supported and loaded; false
80 * otherwise.
82 remoting.HostController.prototype.isPluginSupported = function() {
83 return this.pluginSupported_;
86 /**
87 * @param {function(boolean, boolean, boolean):void} callback Callback to be
88 * called when done.
90 remoting.HostController.prototype.getConsent = function(callback) {
91 this.plugin_.getUsageStatsConsent(callback);
94 /**
95 * Registers and starts the host.
97 * @param {string} hostPin Host PIN.
98 * @param {boolean} consent The user's consent to crash dump reporting.
99 * @param {function(remoting.HostController.AsyncResult):void} callback
100 * callback Callback to be called when done.
101 * @return {void} Nothing.
103 remoting.HostController.prototype.start = function(hostPin, consent, callback) {
104 /** @type {remoting.HostController} */
105 var that = this;
107 /** @return {string} */
108 function generateUuid() {
109 var random = new Uint16Array(8);
110 window.crypto.getRandomValues(random);
111 /** @type {Array.<string>} */
112 var e = new Array();
113 for (var i = 0; i < 8; i++) {
114 e[i] = (/** @type {number} */random[i] + 0x10000).
115 toString(16).substring(1);
117 return e[0] + e[1] + '-' + e[2] + "-" + e[3] + '-' +
118 e[4] + '-' + e[5] + e[6] + e[7];
121 var newHostId = generateUuid();
123 /** @param {function(remoting.HostController.AsyncResult):void} callback
124 * @param {remoting.HostController.AsyncResult} result
125 * @param {string} hostName
126 * @param {string} publicKey */
127 function onStarted(callback, result, hostName, publicKey) {
128 if (result == remoting.HostController.AsyncResult.OK) {
129 remoting.hostList.onLocalHostStarted(hostName, newHostId, publicKey);
130 } else {
131 // Unregister the host if we failed to start it.
132 remoting.HostList.unregisterHostById(newHostId);
134 callback(result);
138 * @param {string} hostName
139 * @param {string} publicKey
140 * @param {string} privateKey
141 * @param {XMLHttpRequest} xhr
143 function onRegistered(hostName, publicKey, privateKey, xhr) {
144 var success = (xhr.status == 200);
146 if (success) {
147 that.plugin_.getPinHash(newHostId, hostPin, startHostWithHash.bind(
148 null, hostName, publicKey, privateKey, xhr));
149 } else {
150 console.log('Failed to register the host. Status: ' + xhr.status +
151 ' response: ' + xhr.responseText);
152 callback(remoting.HostController.AsyncResult.FAILED_DIRECTORY);
157 * @param {string} hostName
158 * @param {string} publicKey
159 * @param {string} privateKey
160 * @param {XMLHttpRequest} xhr
161 * @param {string} hostSecretHash
163 function startHostWithHash(hostName, publicKey, privateKey, xhr,
164 hostSecretHash) {
165 var hostConfig = JSON.stringify({
166 xmpp_login: remoting.identity.getCachedEmail(),
167 oauth_refresh_token: remoting.oauth2.exportRefreshToken(),
168 host_id: newHostId,
169 host_name: hostName,
170 host_secret_hash: hostSecretHash,
171 private_key: privateKey
173 /** @param {remoting.HostController.AsyncResult} result */
174 var onStartDaemon = function(result) {
175 onStarted(callback, result, hostName, publicKey);
177 that.plugin_.startDaemon(hostConfig, consent, onStartDaemon);
181 * @param {string} hostName
182 * @param {string} privateKey
183 * @param {string} publicKey
184 * @param {string} oauthToken
186 function doRegisterHost(hostName, privateKey, publicKey, oauthToken) {
187 var headers = {
188 'Authorization': 'OAuth ' + oauthToken,
189 'Content-type' : 'application/json; charset=UTF-8'
192 var newHostDetails = { data: {
193 hostId: newHostId,
194 hostName: hostName,
195 publicKey: publicKey
196 } };
197 remoting.xhr.post(
198 remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/',
199 /** @param {XMLHttpRequest} xhr */
200 function (xhr) { onRegistered(hostName, publicKey, privateKey, xhr); },
201 JSON.stringify(newHostDetails),
202 headers);
206 * @param {string} hostName
207 * @param {string} privateKey
208 * @param {string} publicKey
210 function onKeyGenerated(hostName, privateKey, publicKey) {
211 remoting.identity.callWithToken(
212 /** @param {string} oauthToken */
213 function(oauthToken) {
214 doRegisterHost(hostName, privateKey, publicKey, oauthToken);
216 /** @param {remoting.Error} error */
217 function(error) {
218 // TODO(jamiewalch): Have a more specific error code here?
219 callback(remoting.HostController.AsyncResult.FAILED);
224 * @param {string} hostName
225 * @return {void} Nothing.
227 function startWithHostname(hostName) {
228 that.plugin_.generateKeyPair(onKeyGenerated.bind(null, hostName));
231 this.plugin_.getHostName(startWithHostname);
235 * Stop the daemon process.
236 * @param {function(remoting.HostController.AsyncResult):void} callback
237 * Callback to be called when finished.
238 * @return {void} Nothing.
240 remoting.HostController.prototype.stop = function(callback) {
241 /** @type {remoting.HostController} */
242 var that = this;
245 * @param {remoting.HostController.AsyncResult} result The result of the
246 * stopDaemon call, to be passed to the callback.
247 * @param {string?} hostId The host id of the local host.
249 function unregisterHost(result, hostId) {
250 if (hostId) {
251 remoting.HostList.unregisterHostById(hostId);
253 callback(result);
257 * @param {remoting.HostController.AsyncResult} result The result of the
258 * stopDaemon call, to be passed to the callback.
260 function onStopped(result) {
261 if (result != remoting.HostController.AsyncResult.OK) {
262 callback(result);
263 return;
265 that.getLocalHostId(unregisterHost.bind(null, result));
268 this.plugin_.stopDaemon(onStopped);
272 * Parse a stringified host configuration and return it as a dictionary if it
273 * is well-formed and contains both host_id and xmpp_login keys. null is
274 * returned if either key is missing, or if the configuration is corrupt.
275 * @param {string} configStr The host configuration, JSON encoded to a string.
276 * @return {Object.<string,string>|null} The host configuration.
278 function parseHostConfig_(configStr) {
279 var config = /** @type {Object.<string,string>} */ jsonParseSafe(configStr);
280 if (config &&
281 typeof config['host_id'] == 'string' &&
282 typeof config['xmpp_login'] == 'string') {
283 return config;
284 } else {
285 // {} means that host is not configured; '' means that the config file could
286 // not be read.
287 // TODO(jamiewalch): '' is expected if the host isn't installed, but should
288 // be reported as an error otherwise. Fix this once we have an event-based
289 // daemon state mechanism.
290 if (configStr != '{}' && configStr != '') {
291 console.error('Invalid getDaemonConfig response.');
294 return null;
298 * @param {string} newPin The new PIN to set
299 * @param {function(remoting.HostController.AsyncResult):void} callback
300 * Callback to be called when finished.
301 * @return {void} Nothing.
303 remoting.HostController.prototype.updatePin = function(newPin, callback) {
304 /** @type {remoting.HostController} */
305 var that = this;
307 /** @param {string} configStr */
308 function onConfig(configStr) {
309 var config = parseHostConfig_(configStr);
310 if (!config) {
311 callback(remoting.HostController.AsyncResult.FAILED);
312 return;
314 var hostId = config['host_id'];
315 that.plugin_.getPinHash(hostId, newPin, updateDaemonConfigWithHash);
318 /** @param {string} pinHash */
319 function updateDaemonConfigWithHash(pinHash) {
320 var newConfig = JSON.stringify({
321 host_secret_hash: pinHash
323 that.plugin_.updateDaemonConfig(newConfig, callback);
326 // TODO(sergeyu): When crbug.com/121518 is fixed: replace this call
327 // with an upriveleged version if that is necessary.
328 this.plugin_.getDaemonConfig(onConfig);
332 * Get the state of the local host.
334 * @param {function(remoting.HostController.State):void} onDone
335 * Completion callback.
337 remoting.HostController.prototype.getLocalHostState = function(onDone) {
338 this.plugin_.getDaemonState(onDone);
342 * Get the id of the local host, or null if it is not registered.
344 * @param {function(string?):void} onDone Completion callback.
346 remoting.HostController.prototype.getLocalHostId = function(onDone) {
347 /** @type {remoting.HostController} */
348 var that = this;
349 /** @param {string} configStr */
350 function onConfig(configStr) {
351 var config = parseHostConfig_(configStr);
352 var hostId = null;
353 if (config) {
354 hostId = config['host_id'];
356 onDone(hostId);
358 try {
359 this.plugin_.getDaemonConfig(onConfig);
360 } catch (err) {
361 onDone(null);
365 /** @type {remoting.HostController} */
366 remoting.hostController = null;