GN + Android: extract android_standalone_library rule.
[chromium-blink-merge.git] / remoting / webapp / crd / js / host_controller.js
blob45ef3660556d749f2e9fecc38a31c7940f3db712
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 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.
17 /** @enum {number} */
18 remoting.HostController.State = {
19 NOT_IMPLEMENTED: -1,
20 NOT_INSTALLED: 0,
21 INSTALLING: 1,
22 STOPPED: 2,
23 STARTING: 3,
24 STARTED: 4,
25 STOPPING: 5,
26 UNKNOWN: 6
29 /**
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];
40 /** @enum {number} */
41 remoting.HostController.AsyncResult = {
42 OK: 0,
43 FAILED: 1,
44 CANCELLED: 2,
45 FAILED_DIRECTORY: 3
48 /**
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];
59 /**
60 * @return {remoting.HostDaemonFacade}
61 * @private
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) {
69 if (version == '') {
70 console.log('Host not installed.');
71 } else {
72 console.log('Host version: ' + version);
76 hostDaemonFacade.getDaemonVersion(printVersion, function() {
77 console.log('Host version not available.');
78 });
80 return hostDaemonFacade;
83 /**
84 * Set of features for which hasFeature() can be used to test.
86 * @enum {string}
88 remoting.HostController.Feature = {
89 PAIRING_REGISTRY: 'pairingRegistry',
90 OAUTH_CLIENT: 'oauthClient'
93 /**
94 * @param {remoting.HostController.Feature} feature The feature to test for.
95 * @param {function(boolean):void} callback
96 * @return {void}
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
106 * called when done.
107 * @param {function(remoting.Error):void} onError Callback to be called on
108 * error.
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
121 * error.
122 * @return {void} Nothing.
124 remoting.HostController.prototype.start = function(hostPin, consent, onDone,
125 onError) {
126 /** @type {remoting.HostController} */
127 var that = this;
129 /** @return {string} */
130 function generateUuid() {
131 var random = new Uint16Array(8);
132 window.crypto.getRandomValues(random);
133 /** @type {Array.<string>} */
134 var e = new Array();
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);
149 onError(error);
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);
160 onDone();
161 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
162 onStartError(remoting.Error.CANCELLED);
163 } else {
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} clientBaseJid
175 * @param {string} hostSecretHash
177 function startHostWithHash(hostName, publicKey, privateKey, xmppLogin,
178 refreshToken, clientBaseJid, hostSecretHash) {
179 var hostConfig = {
180 xmpp_login: xmppLogin,
181 oauth_refresh_token: refreshToken,
182 host_id: newHostId,
183 host_name: hostName,
184 host_secret_hash: hostSecretHash,
185 private_key: privateKey
187 var hostOwner = clientBaseJid;
188 var hostOwnerEmail = remoting.identity.getCachedEmail();
189 if (hostOwner != xmppLogin) {
190 hostConfig['host_owner'] = hostOwner;
191 if (hostOwnerEmail != hostOwner) {
192 hostConfig['host_owner_email'] = hostOwnerEmail;
195 that.hostDaemonFacade_.startDaemon(
196 hostConfig, consent, onStarted.bind(null, hostName, publicKey),
197 onStartError);
201 * @param {string} hostName
202 * @param {string} publicKey
203 * @param {string} privateKey
204 * @param {string} email
205 * @param {string} refreshToken
206 * @param {string} clientBaseJid
208 function onClientBaseJid(
209 hostName, publicKey, privateKey, email, refreshToken, clientBaseJid) {
210 that.hostDaemonFacade_.getPinHash(
211 newHostId, hostPin,
212 startHostWithHash.bind(null, hostName, publicKey, privateKey,
213 email, refreshToken, clientBaseJid),
214 onError);
218 * @param {string} hostName
219 * @param {string} publicKey
220 * @param {string} privateKey
221 * @param {string} email
222 * @param {string} refreshToken
224 function onServiceAccountCredentials(
225 hostName, publicKey, privateKey, email, refreshToken) {
226 that.getClientBaseJid_(
227 onClientBaseJid.bind(
228 null, hostName, publicKey, privateKey, email, refreshToken),
229 onStartError);
233 * @param {string} hostName
234 * @param {string} publicKey
235 * @param {string} privateKey
236 * @param {XMLHttpRequest} xhr
238 function onRegistered(
239 hostName, publicKey, privateKey, xhr) {
240 var success = (xhr.status == 200);
242 if (success) {
243 var result = base.jsonParseSafe(xhr.responseText);
244 if ('data' in result && 'authorizationCode' in result['data']) {
245 that.hostDaemonFacade_.getCredentialsFromAuthCode(
246 result['data']['authorizationCode'],
247 onServiceAccountCredentials.bind(
248 null, hostName, publicKey, privateKey),
249 onError);
250 } else {
251 // No authorization code returned, use regular user credential flow.
252 that.hostDaemonFacade_.getPinHash(
253 newHostId, hostPin, startHostWithHash.bind(
254 null, hostName, publicKey, privateKey,
255 remoting.identity.getCachedEmail(),
256 remoting.oauth2.getRefreshToken(),
257 remoting.identity.getCachedEmail()),
258 onError);
260 } else {
261 console.log('Failed to register the host. Status: ' + xhr.status +
262 ' response: ' + xhr.responseText);
263 onError(remoting.Error.REGISTRATION_FAILED);
268 * @param {string} hostName
269 * @param {string} privateKey
270 * @param {string} publicKey
271 * @param {string} hostClientId
272 * @param {string} oauthToken
274 function doRegisterHost(
275 hostName, privateKey, publicKey, hostClientId, oauthToken) {
276 var headers = {
277 'Authorization': 'OAuth ' + oauthToken,
278 'Content-type' : 'application/json; charset=UTF-8'
281 var newHostDetails = { data: {
282 hostId: newHostId,
283 hostName: hostName,
284 publicKey: publicKey
285 } };
287 var registerHostUrl =
288 remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts';
290 if (hostClientId) {
291 registerHostUrl += '?' + remoting.xhr.urlencodeParamHash(
292 { hostClientId: hostClientId });
295 remoting.xhr.post(
296 registerHostUrl,
297 onRegistered.bind(null, hostName, publicKey, privateKey),
298 JSON.stringify(newHostDetails),
299 headers);
303 * @param {string} hostName
304 * @param {string} privateKey
305 * @param {string} publicKey
306 * @param {string} hostClientId
308 function onHostClientId(
309 hostName, privateKey, publicKey, hostClientId) {
310 remoting.identity.callWithToken(
311 doRegisterHost.bind(
312 null, hostName, privateKey, publicKey, hostClientId), onError);
316 * @param {string} hostName
317 * @param {string} privateKey
318 * @param {string} publicKey
319 * @param {boolean} hasFeature
321 function onHasFeatureOAuthClient(
322 hostName, privateKey, publicKey, hasFeature) {
323 if (hasFeature) {
324 that.hostDaemonFacade_.getHostClientId(
325 onHostClientId.bind(null, hostName, privateKey, publicKey), onError);
326 } else {
327 remoting.identity.callWithToken(
328 doRegisterHost.bind(
329 null, hostName, privateKey, publicKey, null), onError);
334 * @param {string} hostName
335 * @param {string} privateKey
336 * @param {string} publicKey
338 function onKeyGenerated(hostName, privateKey, publicKey) {
339 that.hasFeature(
340 remoting.HostController.Feature.OAUTH_CLIENT,
341 onHasFeatureOAuthClient.bind(null, hostName, privateKey, publicKey));
345 * @param {string} hostName
346 * @return {void} Nothing.
348 function startWithHostname(hostName) {
349 that.hostDaemonFacade_.generateKeyPair(onKeyGenerated.bind(null, hostName),
350 onError);
353 this.hostDaemonFacade_.getHostName(startWithHostname, onError);
357 * Stop the daemon process.
358 * @param {function():void} onDone Callback to be called when done.
359 * @param {function(remoting.Error):void} onError Callback to be called on
360 * error.
361 * @return {void} Nothing.
363 remoting.HostController.prototype.stop = function(onDone, onError) {
364 /** @type {remoting.HostController} */
365 var that = this;
367 /** @param {string?} hostId The host id of the local host. */
368 function unregisterHost(hostId) {
369 if (hostId) {
370 remoting.HostList.unregisterHostById(hostId);
372 onDone();
375 /** @param {remoting.HostController.AsyncResult} result */
376 function onStopped(result) {
377 if (result == remoting.HostController.AsyncResult.OK) {
378 that.getLocalHostId(unregisterHost);
379 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
380 onError(remoting.Error.CANCELLED);
381 } else {
382 onError(remoting.Error.UNEXPECTED);
386 this.hostDaemonFacade_.stopDaemon(onStopped, onError);
390 * Check the host configuration is valid (non-null, and contains both host_id
391 * and xmpp_login keys).
392 * @param {Object} config The host configuration.
393 * @return {boolean} True if it is valid.
395 function isHostConfigValid_(config) {
396 return !!config && typeof config['host_id'] == 'string' &&
397 typeof config['xmpp_login'] == 'string';
401 * @param {string} newPin The new PIN to set
402 * @param {function():void} onDone Callback to be called when done.
403 * @param {function(remoting.Error):void} onError Callback to be called on
404 * error.
405 * @return {void} Nothing.
407 remoting.HostController.prototype.updatePin = function(newPin, onDone,
408 onError) {
409 /** @type {remoting.HostController} */
410 var that = this;
412 /** @param {remoting.HostController.AsyncResult} result */
413 function onConfigUpdated(result) {
414 if (result == remoting.HostController.AsyncResult.OK) {
415 onDone();
416 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
417 onError(remoting.Error.CANCELLED);
418 } else {
419 onError(remoting.Error.UNEXPECTED);
423 /** @param {string} pinHash */
424 function updateDaemonConfigWithHash(pinHash) {
425 var newConfig = {
426 host_secret_hash: pinHash
428 that.hostDaemonFacade_.updateDaemonConfig(newConfig, onConfigUpdated,
429 onError);
432 /** @param {Object} config */
433 function onConfig(config) {
434 if (!isHostConfigValid_(config)) {
435 onError(remoting.Error.UNEXPECTED);
436 return;
438 /** @type {string} */
439 var hostId = config['host_id'];
440 that.hostDaemonFacade_.getPinHash(
441 hostId, newPin, updateDaemonConfigWithHash, onError);
444 // TODO(sergeyu): When crbug.com/121518 is fixed: replace this call
445 // with an unprivileged version if that is necessary.
446 this.hostDaemonFacade_.getDaemonConfig(onConfig, onError);
450 * Get the state of the local host.
452 * @param {function(remoting.HostController.State):void} onDone Completion
453 * callback.
455 remoting.HostController.prototype.getLocalHostState = function(onDone) {
456 /** @param {remoting.Error} error */
457 function onError(error) {
458 onDone((error == remoting.Error.MISSING_PLUGIN) ?
459 remoting.HostController.State.NOT_INSTALLED :
460 remoting.HostController.State.UNKNOWN);
462 this.hostDaemonFacade_.getDaemonState(onDone, onError);
466 * Get the id of the local host, or null if it is not registered.
468 * @param {function(string?):void} onDone Completion callback.
470 remoting.HostController.prototype.getLocalHostId = function(onDone) {
471 /** @type {remoting.HostController} */
472 var that = this;
473 /** @param {Object} config */
474 function onConfig(config) {
475 var hostId = null;
476 if (isHostConfigValid_(config)) {
477 hostId = /** @type {string} */ config['host_id'];
479 onDone(hostId);
482 this.hostDaemonFacade_.getDaemonConfig(onConfig, function(error) {
483 onDone(null);
488 * Fetch the list of paired clients for this host.
490 * @param {function(Array.<remoting.PairedClient>):void} onDone
491 * @param {function(remoting.Error):void} onError
492 * @return {void}
494 remoting.HostController.prototype.getPairedClients = function(onDone,
495 onError) {
496 this.hostDaemonFacade_.getPairedClients(onDone, onError);
500 * Delete a single paired client.
502 * @param {string} client The client id of the pairing to delete.
503 * @param {function():void} onDone Completion callback.
504 * @param {function(remoting.Error):void} onError Error callback.
505 * @return {void}
507 remoting.HostController.prototype.deletePairedClient = function(
508 client, onDone, onError) {
509 this.hostDaemonFacade_.deletePairedClient(client, onDone, onError);
513 * Delete all paired clients.
515 * @param {function():void} onDone Completion callback.
516 * @param {function(remoting.Error):void} onError Error callback.
517 * @return {void}
519 remoting.HostController.prototype.clearPairedClients = function(
520 onDone, onError) {
521 this.hostDaemonFacade_.clearPairedClients(onDone, onError);
525 * Gets the host owner's base JID, used by the host for client authorization.
526 * In most cases this is the same as the owner's email address, but for
527 * non-Gmail accounts, it may be different.
529 * @private
530 * @param {function(string): void} onSuccess
531 * @param {function(remoting.Error): void} onError
533 remoting.HostController.prototype.getClientBaseJid_ = function(
534 onSuccess, onError) {
535 /** @type {remoting.SignalStrategy} */
536 var signalStrategy = null;
538 /** @param {remoting.SignalStrategy.State} state */
539 var onState = function(state) {
540 switch (state) {
541 case remoting.SignalStrategy.State.CONNECTED:
542 var jid = signalStrategy.getJid().split('/')[0].toLowerCase();
543 base.dispose(signalStrategy);
544 signalStrategy = null;
545 onSuccess(jid);
546 break;
548 case remoting.SignalStrategy.State.FAILED:
549 var error = signalStrategy.getError();
550 base.dispose(signalStrategy);
551 signalStrategy = null;
552 onError(error);
553 break;
557 signalStrategy = remoting.SignalStrategy.create(onState);
559 /** @param {string} token */
560 function connectSignalingWithToken(token) {
561 remoting.identity.getEmail(
562 connectSignalingWithTokenAndEmail.bind(null, token), onError);
566 * @param {string} token
567 * @param {string} email
569 function connectSignalingWithTokenAndEmail(token, email) {
570 signalStrategy.connect(
571 remoting.settings.XMPP_SERVER_ADDRESS, email, token);
574 remoting.identity.callWithToken(connectSignalingWithToken, onError);
577 /** @type {remoting.HostController} */
578 remoting.hostController = null;