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.
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
11 remoting.HostController = function() {
12 /** @return {remoting.HostPlugin} */
13 var createNpapiPlugin = function() {
14 var plugin = remoting.HostSession.createPlugin();
15 /** @type {HTMLElement} @private */
16 var container = document.getElementById('daemon-plugin-container');
17 container.appendChild(plugin);
21 /** @type {remoting.HostDispatcher} @private */
22 this.hostDispatcher_ = new remoting.HostDispatcher(createNpapiPlugin);
24 /** @param {string} version */
25 var printVersion = function(version) {
27 console.log('Host not installed.');
29 console.log('Host version: ' + version);
33 this.hostDispatcher_.getDaemonVersion(printVersion, function() {
34 console.log('Host version not available.');
38 // Note that the values in the enums below are copied from
39 // daemon_controller.h and must be kept in sync.
41 remoting.HostController.State = {
53 remoting.HostController.AsyncResult = {
61 * Set of features for which hasFeature() can be used to test.
65 remoting.HostController.Feature = {
66 PAIRING_REGISTRY: 'pairingRegistry',
67 OAUTH_CLIENT: 'oauthClient'
71 * @param {remoting.HostController.Feature} feature The feature to test for.
72 * @param {function(boolean):void} callback
75 remoting.HostController.prototype.hasFeature = function(feature, callback) {
76 // TODO(rmsousa): This could synchronously return a boolean, provided it were
77 // only called after the dispatcher is completely initialized.
78 this.hostDispatcher_.hasFeature(feature, callback);
82 * @param {function(boolean, boolean, boolean):void} onDone Callback to be
84 * @param {function(remoting.Error):void} onError Callback to be called on
87 remoting.HostController.prototype.getConsent = function(onDone, onError) {
88 this.hostDispatcher_.getUsageStatsConsent(onDone, onError);
92 * Registers and starts the host.
94 * @param {string} hostPin Host PIN.
95 * @param {boolean} consent The user's consent to crash dump reporting.
96 * @param {function():void} onDone Callback to be called when done.
97 * @param {function(remoting.Error):void} onError Callback to be called on
99 * @return {void} Nothing.
101 remoting.HostController.prototype.start = function(hostPin, consent, onDone,
103 /** @type {remoting.HostController} */
106 /** @return {string} */
107 function generateUuid() {
108 var random = new Uint16Array(8);
109 window.crypto.getRandomValues(random);
110 /** @type {Array.<string>} */
112 for (var i = 0; i < 8; i++) {
113 e[i] = (/** @type {number} */random[i] + 0x10000).
114 toString(16).substring(1);
116 return e[0] + e[1] + '-' + e[2] + '-' + e[3] + '-' +
117 e[4] + '-' + e[5] + e[6] + e[7];
120 var newHostId = generateUuid();
122 /** @param {remoting.Error} error */
123 function onStartError(error) {
124 // Unregister the host if we failed to start it.
125 remoting.HostList.unregisterHostById(newHostId);
130 * @param {string} hostName
131 * @param {string} publicKey
132 * @param {remoting.HostController.AsyncResult} result
134 function onStarted(hostName, publicKey, result) {
135 if (result == remoting.HostController.AsyncResult.OK) {
136 remoting.hostList.onLocalHostStarted(hostName, newHostId, publicKey);
138 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
139 onStartError(remoting.Error.CANCELLED);
141 onStartError(remoting.Error.UNEXPECTED);
146 * @param {string} hostName
147 * @param {string} publicKey
148 * @param {string} privateKey
149 * @param {string} xmppLogin
150 * @param {string} refreshToken
151 * @param {string} hostSecretHash
153 function startHostWithHash(hostName, publicKey, privateKey,
154 xmppLogin, refreshToken, hostSecretHash) {
156 xmpp_login: xmppLogin,
157 oauth_refresh_token: refreshToken,
160 host_secret_hash: hostSecretHash,
161 private_key: privateKey
163 var hostOwner = remoting.identity.getCachedEmail();
164 if (hostOwner != xmppLogin) {
165 hostConfig['host_owner'] = hostOwner;
167 that.hostDispatcher_.startDaemon(hostConfig, consent,
168 onStarted.bind(null, hostName, publicKey),
173 * @param {string} hostName
174 * @param {string} publicKey
175 * @param {string} privateKey
176 * @param {string} email
177 * @param {string} refreshToken
179 function onServiceAccountCredentials(
180 hostName, publicKey, privateKey, email, refreshToken) {
181 that.hostDispatcher_.getPinHash(
183 startHostWithHash.bind(
184 null, hostName, publicKey, privateKey, email, refreshToken),
189 * @param {string} hostName
190 * @param {string} publicKey
191 * @param {string} privateKey
192 * @param {XMLHttpRequest} xhr
194 function onRegistered(
195 hostName, publicKey, privateKey, xhr) {
196 var success = (xhr.status == 200);
199 var result = jsonParseSafe(xhr.responseText);
200 if ('data' in result && 'authorizationCode' in result['data']) {
201 that.hostDispatcher_.getCredentialsFromAuthCode(
202 result['data']['authorizationCode'],
203 onServiceAccountCredentials.bind(
204 null, hostName, publicKey, privateKey),
207 // No authorization code returned, use regular user credential flow.
208 that.hostDispatcher_.getPinHash(
209 newHostId, hostPin, startHostWithHash.bind(
210 null, hostName, publicKey, privateKey,
211 remoting.identity.getCachedEmail(),
212 remoting.oauth2.exportRefreshToken()),
216 console.log('Failed to register the host. Status: ' + xhr.status +
217 ' response: ' + xhr.responseText);
218 onError(remoting.Error.REGISTRATION_FAILED);
223 * @param {string} hostName
224 * @param {string} privateKey
225 * @param {string} publicKey
226 * @param {string} hostClientId
227 * @param {string} oauthToken
229 function doRegisterHost(
230 hostName, privateKey, publicKey, hostClientId, oauthToken) {
232 'Authorization': 'OAuth ' + oauthToken,
233 'Content-type' : 'application/json; charset=UTF-8'
236 var newHostDetails = { data: {
242 var registerHostUrl =
243 remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts';
246 registerHostUrl += '?' + remoting.xhr.urlencodeParamHash(
247 { hostClientId: hostClientId });
252 onRegistered.bind(null, hostName, publicKey, privateKey),
253 JSON.stringify(newHostDetails),
258 * @param {string} hostName
259 * @param {string} privateKey
260 * @param {string} publicKey
261 * @param {string} hostClientId
263 function onHostClientId(
264 hostName, privateKey, publicKey, hostClientId) {
265 remoting.identity.callWithToken(
267 null, hostName, privateKey, publicKey, hostClientId), onError);
271 * @param {string} hostName
272 * @param {string} privateKey
273 * @param {string} publicKey
274 * @param {boolean} hasFeature
276 function onHasFeatureOAuthClient(
277 hostName, privateKey, publicKey, hasFeature) {
279 that.hostDispatcher_.getHostClientId(
280 onHostClientId.bind(null, hostName, privateKey, publicKey), onError);
282 remoting.identity.callWithToken(
284 null, hostName, privateKey, publicKey, null), onError);
289 * @param {string} hostName
290 * @param {string} privateKey
291 * @param {string} publicKey
293 function onKeyGenerated(hostName, privateKey, publicKey) {
295 remoting.HostController.Feature.OAUTH_CLIENT,
296 onHasFeatureOAuthClient.bind(null, hostName, privateKey, publicKey));
300 * @param {string} hostName
301 * @return {void} Nothing.
303 function startWithHostname(hostName) {
304 that.hostDispatcher_.generateKeyPair(onKeyGenerated.bind(null, hostName),
308 this.hostDispatcher_.getHostName(startWithHostname, onError);
312 * Stop the daemon process.
313 * @param {function():void} onDone Callback to be called when done.
314 * @param {function(remoting.Error):void} onError Callback to be called on
316 * @return {void} Nothing.
318 remoting.HostController.prototype.stop = function(onDone, onError) {
319 /** @type {remoting.HostController} */
322 /** @param {string?} hostId The host id of the local host. */
323 function unregisterHost(hostId) {
325 remoting.HostList.unregisterHostById(hostId);
330 /** @param {remoting.HostController.AsyncResult} result */
331 function onStopped(result) {
332 if (result == remoting.HostController.AsyncResult.OK) {
333 that.getLocalHostId(unregisterHost);
334 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
335 onError(remoting.Error.CANCELLED);
337 onError(remoting.Error.UNEXPECTED);
341 this.hostDispatcher_.stopDaemon(onStopped, onError);
345 * Check the host configuration is valid (non-null, and contains both host_id
346 * and xmpp_login keys).
347 * @param {Object} config The host configuration.
348 * @return {boolean} True if it is valid.
350 function isHostConfigValid_(config) {
351 return !!config && typeof config['host_id'] == 'string' &&
352 typeof config['xmpp_login'] == 'string';
356 * @param {string} newPin The new PIN to set
357 * @param {function():void} onDone Callback to be called when done.
358 * @param {function(remoting.Error):void} onError Callback to be called on
360 * @return {void} Nothing.
362 remoting.HostController.prototype.updatePin = function(newPin, onDone,
364 /** @type {remoting.HostController} */
367 /** @param {remoting.HostController.AsyncResult} result */
368 function onConfigUpdated(result) {
369 if (result == remoting.HostController.AsyncResult.OK) {
371 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
372 onError(remoting.Error.CANCELLED);
374 onError(remoting.Error.UNEXPECTED);
378 /** @param {string} pinHash */
379 function updateDaemonConfigWithHash(pinHash) {
381 host_secret_hash: pinHash
383 that.hostDispatcher_.updateDaemonConfig(newConfig, onConfigUpdated,
387 /** @param {Object} config */
388 function onConfig(config) {
389 if (!isHostConfigValid_(config)) {
390 onError(remoting.Error.UNEXPECTED);
393 /** @type {string} */
394 var hostId = config['host_id'];
395 that.hostDispatcher_.getPinHash(hostId, newPin, updateDaemonConfigWithHash,
399 // TODO(sergeyu): When crbug.com/121518 is fixed: replace this call
400 // with an unprivileged version if that is necessary.
401 this.hostDispatcher_.getDaemonConfig(onConfig, onError);
405 * Get the state of the local host.
407 * @param {function(remoting.HostController.State):void} onDone Completion
410 remoting.HostController.prototype.getLocalHostState = function(onDone) {
411 this.hostDispatcher_.getDaemonState(onDone, function() {
412 onDone(remoting.HostController.State.NOT_IMPLEMENTED);
417 * Get the id of the local host, or null if it is not registered.
419 * @param {function(string?):void} onDone Completion callback.
421 remoting.HostController.prototype.getLocalHostId = function(onDone) {
422 /** @type {remoting.HostController} */
424 /** @param {Object} config */
425 function onConfig(config) {
427 if (isHostConfigValid_(config)) {
428 hostId = /** @type {string} */ config['host_id'];
433 this.hostDispatcher_.getDaemonConfig(onConfig, function(error) {
439 * Fetch the list of paired clients for this host.
441 * @param {function(Array.<remoting.PairedClient>):void} onDone
442 * @param {function(remoting.Error):void} onError
445 remoting.HostController.prototype.getPairedClients = function(onDone,
447 this.hostDispatcher_.getPairedClients(onDone, onError);
451 * Delete a single paired client.
453 * @param {string} client The client id of the pairing to delete.
454 * @param {function():void} onDone Completion callback.
455 * @param {function(remoting.Error):void} onError Error callback.
458 remoting.HostController.prototype.deletePairedClient = function(
459 client, onDone, onError) {
460 this.hostDispatcher_.deletePairedClient(client, onDone, onError);
464 * Delete all paired clients.
466 * @param {function():void} onDone Completion callback.
467 * @param {function(remoting.Error):void} onError Error callback.
470 remoting.HostController.prototype.clearPairedClients = function(
472 this.hostDispatcher_.clearPairedClients(onDone, onError);
475 /** @type {remoting.HostController} */
476 remoting.hostController = null;