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 this.hostDaemonFacade_ = this.createDaemonFacade_();
15 // Note that the values in the enums below are copied from
16 // daemon_controller.h and must be kept in sync.
18 remoting.HostController.State = {
30 * @param {string} state The host controller state name.
31 * @return {remoting.HostController.State} The state enum value.
33 remoting.HostController.State.fromString = function(state) {
34 if (!remoting.HostController.State.hasOwnProperty(state)) {
35 throw "Invalid HostController.State: " + state;
37 return remoting.HostController.State[state];
41 remoting.HostController.AsyncResult = {
49 * @param {string} result The async result name.
50 * @return {remoting.HostController.AsyncResult} The result enum value.
52 remoting.HostController.AsyncResult.fromString = function(result) {
53 if (!remoting.HostController.AsyncResult.hasOwnProperty(result)) {
54 throw "Invalid HostController.AsyncResult: " + result;
56 return remoting.HostController.AsyncResult[result];
60 * @return {remoting.HostDaemonFacade}
63 remoting.HostController.prototype.createDaemonFacade_ = function() {
64 /** @type {remoting.HostDaemonFacade} @private */
65 var hostDaemonFacade = new remoting.HostDaemonFacade();
67 /** @param {string} version */
68 var printVersion = function(version) {
70 console.log('Host not installed.');
72 console.log('Host version: ' + version);
76 hostDaemonFacade.getDaemonVersion(printVersion, function() {
77 console.log('Host version not available.');
80 return hostDaemonFacade;
84 * Set of features for which hasFeature() can be used to test.
88 remoting.HostController.Feature = {
89 PAIRING_REGISTRY: 'pairingRegistry',
90 OAUTH_CLIENT: 'oauthClient'
94 * @param {remoting.HostController.Feature} feature The feature to test for.
95 * @param {function(boolean):void} callback
98 remoting.HostController.prototype.hasFeature = function(feature, callback) {
99 // TODO(rmsousa): This could synchronously return a boolean, provided it were
100 // only called after native messaging is completely initialized.
101 this.hostDaemonFacade_.hasFeature(feature, callback);
105 * @param {function(boolean, boolean, boolean):void} onDone Callback to be
107 * @param {function(remoting.Error):void} onError Callback to be called on
110 remoting.HostController.prototype.getConsent = function(onDone, onError) {
111 this.hostDaemonFacade_.getUsageStatsConsent(onDone, onError);
115 * Registers and starts the host.
117 * @param {string} hostPin Host PIN.
118 * @param {boolean} consent The user's consent to crash dump reporting.
119 * @param {function():void} onDone Callback to be called when done.
120 * @param {function(remoting.Error):void} onError Callback to be called on
122 * @return {void} Nothing.
124 remoting.HostController.prototype.start = function(hostPin, consent, onDone,
126 /** @type {remoting.HostController} */
129 /** @return {string} */
130 function generateUuid() {
131 var random = new Uint16Array(8);
132 window.crypto.getRandomValues(random);
133 /** @type {Array.<string>} */
135 for (var i = 0; i < 8; i++) {
136 e[i] = (/** @type {number} */random[i] + 0x10000).
137 toString(16).substring(1);
139 return e[0] + e[1] + '-' + e[2] + '-' + e[3] + '-' +
140 e[4] + '-' + e[5] + e[6] + e[7];
143 var newHostId = generateUuid();
145 /** @param {remoting.Error} error */
146 function onStartError(error) {
147 // Unregister the host if we failed to start it.
148 remoting.HostList.unregisterHostById(newHostId);
153 * @param {string} hostName
154 * @param {string} publicKey
155 * @param {remoting.HostController.AsyncResult} result
157 function onStarted(hostName, publicKey, result) {
158 if (result == remoting.HostController.AsyncResult.OK) {
159 remoting.hostList.onLocalHostStarted(hostName, newHostId, publicKey);
161 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
162 onStartError(remoting.Error.CANCELLED);
164 onStartError(remoting.Error.UNEXPECTED);
169 * @param {string} hostName
170 * @param {string} publicKey
171 * @param {string} privateKey
172 * @param {string} xmppLogin
173 * @param {string} refreshToken
174 * @param {string} hostSecretHash
176 function startHostWithHash(hostName, publicKey, privateKey,
177 xmppLogin, refreshToken, hostSecretHash) {
179 xmpp_login: xmppLogin,
180 oauth_refresh_token: refreshToken,
183 host_secret_hash: hostSecretHash,
184 private_key: privateKey
186 var hostOwner = remoting.identity.getCachedEmail();
187 if (hostOwner != xmppLogin) {
188 hostConfig['host_owner'] = hostOwner;
190 that.hostDaemonFacade_.startDaemon(
191 hostConfig, consent, onStarted.bind(null, hostName, publicKey),
196 * @param {string} hostName
197 * @param {string} publicKey
198 * @param {string} privateKey
199 * @param {string} email
200 * @param {string} refreshToken
202 function onServiceAccountCredentials(
203 hostName, publicKey, privateKey, email, refreshToken) {
204 that.hostDaemonFacade_.getPinHash(
206 startHostWithHash.bind(
207 null, hostName, publicKey, privateKey, email, refreshToken),
212 * @param {string} hostName
213 * @param {string} publicKey
214 * @param {string} privateKey
215 * @param {XMLHttpRequest} xhr
217 function onRegistered(
218 hostName, publicKey, privateKey, xhr) {
219 var success = (xhr.status == 200);
222 var result = jsonParseSafe(xhr.responseText);
223 if ('data' in result && 'authorizationCode' in result['data']) {
224 that.hostDaemonFacade_.getCredentialsFromAuthCode(
225 result['data']['authorizationCode'],
226 onServiceAccountCredentials.bind(
227 null, hostName, publicKey, privateKey),
230 // No authorization code returned, use regular user credential flow.
231 that.hostDaemonFacade_.getPinHash(
232 newHostId, hostPin, startHostWithHash.bind(
233 null, hostName, publicKey, privateKey,
234 remoting.identity.getCachedEmail(),
235 remoting.oauth2.getRefreshToken()),
239 console.log('Failed to register the host. Status: ' + xhr.status +
240 ' response: ' + xhr.responseText);
241 onError(remoting.Error.REGISTRATION_FAILED);
246 * @param {string} hostName
247 * @param {string} privateKey
248 * @param {string} publicKey
249 * @param {string} hostClientId
250 * @param {string} oauthToken
252 function doRegisterHost(
253 hostName, privateKey, publicKey, hostClientId, oauthToken) {
255 'Authorization': 'OAuth ' + oauthToken,
256 'Content-type' : 'application/json; charset=UTF-8'
259 var newHostDetails = { data: {
265 var registerHostUrl =
266 remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts';
269 registerHostUrl += '?' + remoting.xhr.urlencodeParamHash(
270 { hostClientId: hostClientId });
275 onRegistered.bind(null, hostName, publicKey, privateKey),
276 JSON.stringify(newHostDetails),
281 * @param {string} hostName
282 * @param {string} privateKey
283 * @param {string} publicKey
284 * @param {string} hostClientId
286 function onHostClientId(
287 hostName, privateKey, publicKey, hostClientId) {
288 remoting.identity.callWithToken(
290 null, hostName, privateKey, publicKey, hostClientId), onError);
294 * @param {string} hostName
295 * @param {string} privateKey
296 * @param {string} publicKey
297 * @param {boolean} hasFeature
299 function onHasFeatureOAuthClient(
300 hostName, privateKey, publicKey, hasFeature) {
302 that.hostDaemonFacade_.getHostClientId(
303 onHostClientId.bind(null, hostName, privateKey, publicKey), onError);
305 remoting.identity.callWithToken(
307 null, hostName, privateKey, publicKey, null), onError);
312 * @param {string} hostName
313 * @param {string} privateKey
314 * @param {string} publicKey
316 function onKeyGenerated(hostName, privateKey, publicKey) {
318 remoting.HostController.Feature.OAUTH_CLIENT,
319 onHasFeatureOAuthClient.bind(null, hostName, privateKey, publicKey));
323 * @param {string} hostName
324 * @return {void} Nothing.
326 function startWithHostname(hostName) {
327 that.hostDaemonFacade_.generateKeyPair(onKeyGenerated.bind(null, hostName),
331 this.hostDaemonFacade_.getHostName(startWithHostname, onError);
335 * Stop the daemon process.
336 * @param {function():void} onDone Callback to be called when done.
337 * @param {function(remoting.Error):void} onError Callback to be called on
339 * @return {void} Nothing.
341 remoting.HostController.prototype.stop = function(onDone, onError) {
342 /** @type {remoting.HostController} */
345 /** @param {string?} hostId The host id of the local host. */
346 function unregisterHost(hostId) {
348 remoting.HostList.unregisterHostById(hostId);
353 /** @param {remoting.HostController.AsyncResult} result */
354 function onStopped(result) {
355 if (result == remoting.HostController.AsyncResult.OK) {
356 that.getLocalHostId(unregisterHost);
357 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
358 onError(remoting.Error.CANCELLED);
360 onError(remoting.Error.UNEXPECTED);
364 this.hostDaemonFacade_.stopDaemon(onStopped, onError);
368 * Check the host configuration is valid (non-null, and contains both host_id
369 * and xmpp_login keys).
370 * @param {Object} config The host configuration.
371 * @return {boolean} True if it is valid.
373 function isHostConfigValid_(config) {
374 return !!config && typeof config['host_id'] == 'string' &&
375 typeof config['xmpp_login'] == 'string';
379 * @param {string} newPin The new PIN to set
380 * @param {function():void} onDone Callback to be called when done.
381 * @param {function(remoting.Error):void} onError Callback to be called on
383 * @return {void} Nothing.
385 remoting.HostController.prototype.updatePin = function(newPin, onDone,
387 /** @type {remoting.HostController} */
390 /** @param {remoting.HostController.AsyncResult} result */
391 function onConfigUpdated(result) {
392 if (result == remoting.HostController.AsyncResult.OK) {
394 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
395 onError(remoting.Error.CANCELLED);
397 onError(remoting.Error.UNEXPECTED);
401 /** @param {string} pinHash */
402 function updateDaemonConfigWithHash(pinHash) {
404 host_secret_hash: pinHash
406 that.hostDaemonFacade_.updateDaemonConfig(newConfig, onConfigUpdated,
410 /** @param {Object} config */
411 function onConfig(config) {
412 if (!isHostConfigValid_(config)) {
413 onError(remoting.Error.UNEXPECTED);
416 /** @type {string} */
417 var hostId = config['host_id'];
418 that.hostDaemonFacade_.getPinHash(
419 hostId, newPin, updateDaemonConfigWithHash, onError);
422 // TODO(sergeyu): When crbug.com/121518 is fixed: replace this call
423 // with an unprivileged version if that is necessary.
424 this.hostDaemonFacade_.getDaemonConfig(onConfig, onError);
428 * Get the state of the local host.
430 * @param {function(remoting.HostController.State):void} onDone Completion
433 remoting.HostController.prototype.getLocalHostState = function(onDone) {
434 /** @param {remoting.Error} error */
435 function onError(error) {
436 onDone((error == remoting.Error.MISSING_PLUGIN) ?
437 remoting.HostController.State.NOT_INSTALLED :
438 remoting.HostController.State.UNKNOWN);
440 this.hostDaemonFacade_.getDaemonState(onDone, onError);
444 * Get the id of the local host, or null if it is not registered.
446 * @param {function(string?):void} onDone Completion callback.
448 remoting.HostController.prototype.getLocalHostId = function(onDone) {
449 /** @type {remoting.HostController} */
451 /** @param {Object} config */
452 function onConfig(config) {
454 if (isHostConfigValid_(config)) {
455 hostId = /** @type {string} */ config['host_id'];
460 this.hostDaemonFacade_.getDaemonConfig(onConfig, function(error) {
466 * Fetch the list of paired clients for this host.
468 * @param {function(Array.<remoting.PairedClient>):void} onDone
469 * @param {function(remoting.Error):void} onError
472 remoting.HostController.prototype.getPairedClients = function(onDone,
474 this.hostDaemonFacade_.getPairedClients(onDone, onError);
478 * Delete a single paired client.
480 * @param {string} client The client id of the pairing to delete.
481 * @param {function():void} onDone Completion callback.
482 * @param {function(remoting.Error):void} onError Error callback.
485 remoting.HostController.prototype.deletePairedClient = function(
486 client, onDone, onError) {
487 this.hostDaemonFacade_.deletePairedClient(client, onDone, onError);
491 * Delete all paired clients.
493 * @param {function():void} onDone Completion callback.
494 * @param {function(remoting.Error):void} onError Error callback.
497 remoting.HostController.prototype.clearPairedClients = function(
499 this.hostDaemonFacade_.clearPairedClients(onDone, onError);
502 /** @type {remoting.HostController} */
503 remoting.hostController = null;