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 createPluginForMe2Me = function() {
14 /** @type {HTMLElement} @private */
15 var container = document.getElementById('daemon-plugin-container');
16 return remoting.createNpapiPlugin(container);
19 /** @type {remoting.HostDispatcher} @private */
20 this.hostDispatcher_ = new remoting.HostDispatcher(createPluginForMe2Me);
22 /** @param {string} version */
23 var printVersion = function(version) {
25 console.log('Host not installed.');
27 console.log('Host version: ' + version);
31 this.hostDispatcher_.getDaemonVersion(printVersion, function() {
32 console.log('Host version not available.');
36 // Note that the values in the enums below are copied from
37 // daemon_controller.h and must be kept in sync.
39 remoting.HostController.State = {
51 * @param {string} state The host controller state name.
52 * @return {remoting.HostController.State} The state enum value.
54 remoting.HostController.State.fromString = function(state) {
55 if (!remoting.HostController.State.hasOwnProperty(state)) {
56 throw "Invalid HostController.State: " + state;
58 return remoting.HostController.State[state];
62 remoting.HostController.AsyncResult = {
70 * @param {string} result The async result name.
71 * @return {remoting.HostController.AsyncResult} The result enum value.
73 remoting.HostController.AsyncResult.fromString = function(result) {
74 if (!remoting.HostController.AsyncResult.hasOwnProperty(result)) {
75 throw "Invalid HostController.AsyncResult: " + result;
77 return remoting.HostController.AsyncResult[result];
81 * Set of features for which hasFeature() can be used to test.
85 remoting.HostController.Feature = {
86 PAIRING_REGISTRY: 'pairingRegistry',
87 OAUTH_CLIENT: 'oauthClient'
91 * @param {remoting.HostController.Feature} feature The feature to test for.
92 * @param {function(boolean):void} callback
95 remoting.HostController.prototype.hasFeature = function(feature, callback) {
96 // TODO(rmsousa): This could synchronously return a boolean, provided it were
97 // only called after the dispatcher is completely initialized.
98 this.hostDispatcher_.hasFeature(feature, callback);
102 * @param {function(boolean, boolean, boolean):void} onDone Callback to be
104 * @param {function(remoting.Error):void} onError Callback to be called on
107 remoting.HostController.prototype.getConsent = function(onDone, onError) {
108 this.hostDispatcher_.getUsageStatsConsent(onDone, onError);
112 * Registers and starts the host.
114 * @param {string} hostPin Host PIN.
115 * @param {boolean} consent The user's consent to crash dump reporting.
116 * @param {function():void} onDone Callback to be called when done.
117 * @param {function(remoting.Error):void} onError Callback to be called on
119 * @return {void} Nothing.
121 remoting.HostController.prototype.start = function(hostPin, consent, onDone,
123 /** @type {remoting.HostController} */
126 /** @return {string} */
127 function generateUuid() {
128 var random = new Uint16Array(8);
129 window.crypto.getRandomValues(random);
130 /** @type {Array.<string>} */
132 for (var i = 0; i < 8; i++) {
133 e[i] = (/** @type {number} */random[i] + 0x10000).
134 toString(16).substring(1);
136 return e[0] + e[1] + '-' + e[2] + '-' + e[3] + '-' +
137 e[4] + '-' + e[5] + e[6] + e[7];
140 var newHostId = generateUuid();
142 /** @param {remoting.Error} error */
143 function onStartError(error) {
144 // Unregister the host if we failed to start it.
145 remoting.HostList.unregisterHostById(newHostId);
150 * @param {string} hostName
151 * @param {string} publicKey
152 * @param {remoting.HostController.AsyncResult} result
154 function onStarted(hostName, publicKey, result) {
155 if (result == remoting.HostController.AsyncResult.OK) {
156 remoting.hostList.onLocalHostStarted(hostName, newHostId, publicKey);
158 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
159 onStartError(remoting.Error.CANCELLED);
161 onStartError(remoting.Error.UNEXPECTED);
166 * @param {string} hostName
167 * @param {string} publicKey
168 * @param {string} privateKey
169 * @param {string} xmppLogin
170 * @param {string} refreshToken
171 * @param {string} hostSecretHash
173 function startHostWithHash(hostName, publicKey, privateKey,
174 xmppLogin, refreshToken, hostSecretHash) {
176 xmpp_login: xmppLogin,
177 oauth_refresh_token: refreshToken,
180 host_secret_hash: hostSecretHash,
181 private_key: privateKey
183 var hostOwner = remoting.identity.getCachedEmail();
184 if (hostOwner != xmppLogin) {
185 hostConfig['host_owner'] = hostOwner;
187 that.hostDispatcher_.startDaemon(hostConfig, consent,
188 onStarted.bind(null, hostName, publicKey),
193 * @param {string} hostName
194 * @param {string} publicKey
195 * @param {string} privateKey
196 * @param {string} email
197 * @param {string} refreshToken
199 function onServiceAccountCredentials(
200 hostName, publicKey, privateKey, email, refreshToken) {
201 that.hostDispatcher_.getPinHash(
203 startHostWithHash.bind(
204 null, hostName, publicKey, privateKey, email, refreshToken),
209 * @param {string} hostName
210 * @param {string} publicKey
211 * @param {string} privateKey
212 * @param {XMLHttpRequest} xhr
214 function onRegistered(
215 hostName, publicKey, privateKey, xhr) {
216 var success = (xhr.status == 200);
219 var result = jsonParseSafe(xhr.responseText);
220 if ('data' in result && 'authorizationCode' in result['data']) {
221 that.hostDispatcher_.getCredentialsFromAuthCode(
222 result['data']['authorizationCode'],
223 onServiceAccountCredentials.bind(
224 null, hostName, publicKey, privateKey),
227 // No authorization code returned, use regular user credential flow.
228 that.hostDispatcher_.getPinHash(
229 newHostId, hostPin, startHostWithHash.bind(
230 null, hostName, publicKey, privateKey,
231 remoting.identity.getCachedEmail(),
232 remoting.oauth2.getRefreshToken()),
236 console.log('Failed to register the host. Status: ' + xhr.status +
237 ' response: ' + xhr.responseText);
238 onError(remoting.Error.REGISTRATION_FAILED);
243 * @param {string} hostName
244 * @param {string} privateKey
245 * @param {string} publicKey
246 * @param {string} hostClientId
247 * @param {string} oauthToken
249 function doRegisterHost(
250 hostName, privateKey, publicKey, hostClientId, oauthToken) {
252 'Authorization': 'OAuth ' + oauthToken,
253 'Content-type' : 'application/json; charset=UTF-8'
256 var newHostDetails = { data: {
262 var registerHostUrl =
263 remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts';
266 registerHostUrl += '?' + remoting.xhr.urlencodeParamHash(
267 { hostClientId: hostClientId });
272 onRegistered.bind(null, hostName, publicKey, privateKey),
273 JSON.stringify(newHostDetails),
278 * @param {string} hostName
279 * @param {string} privateKey
280 * @param {string} publicKey
281 * @param {string} hostClientId
283 function onHostClientId(
284 hostName, privateKey, publicKey, hostClientId) {
285 remoting.identity.callWithToken(
287 null, hostName, privateKey, publicKey, hostClientId), onError);
291 * @param {string} hostName
292 * @param {string} privateKey
293 * @param {string} publicKey
294 * @param {boolean} hasFeature
296 function onHasFeatureOAuthClient(
297 hostName, privateKey, publicKey, hasFeature) {
299 that.hostDispatcher_.getHostClientId(
300 onHostClientId.bind(null, hostName, privateKey, publicKey), onError);
302 remoting.identity.callWithToken(
304 null, hostName, privateKey, publicKey, null), onError);
309 * @param {string} hostName
310 * @param {string} privateKey
311 * @param {string} publicKey
313 function onKeyGenerated(hostName, privateKey, publicKey) {
315 remoting.HostController.Feature.OAUTH_CLIENT,
316 onHasFeatureOAuthClient.bind(null, hostName, privateKey, publicKey));
320 * @param {string} hostName
321 * @return {void} Nothing.
323 function startWithHostname(hostName) {
324 that.hostDispatcher_.generateKeyPair(onKeyGenerated.bind(null, hostName),
328 this.hostDispatcher_.getHostName(startWithHostname, onError);
332 * Stop the daemon process.
333 * @param {function():void} onDone Callback to be called when done.
334 * @param {function(remoting.Error):void} onError Callback to be called on
336 * @return {void} Nothing.
338 remoting.HostController.prototype.stop = function(onDone, onError) {
339 /** @type {remoting.HostController} */
342 /** @param {string?} hostId The host id of the local host. */
343 function unregisterHost(hostId) {
345 remoting.HostList.unregisterHostById(hostId);
350 /** @param {remoting.HostController.AsyncResult} result */
351 function onStopped(result) {
352 if (result == remoting.HostController.AsyncResult.OK) {
353 that.getLocalHostId(unregisterHost);
354 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
355 onError(remoting.Error.CANCELLED);
357 onError(remoting.Error.UNEXPECTED);
361 this.hostDispatcher_.stopDaemon(onStopped, onError);
365 * Check the host configuration is valid (non-null, and contains both host_id
366 * and xmpp_login keys).
367 * @param {Object} config The host configuration.
368 * @return {boolean} True if it is valid.
370 function isHostConfigValid_(config) {
371 return !!config && typeof config['host_id'] == 'string' &&
372 typeof config['xmpp_login'] == 'string';
376 * @param {string} newPin The new PIN to set
377 * @param {function():void} onDone Callback to be called when done.
378 * @param {function(remoting.Error):void} onError Callback to be called on
380 * @return {void} Nothing.
382 remoting.HostController.prototype.updatePin = function(newPin, onDone,
384 /** @type {remoting.HostController} */
387 /** @param {remoting.HostController.AsyncResult} result */
388 function onConfigUpdated(result) {
389 if (result == remoting.HostController.AsyncResult.OK) {
391 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
392 onError(remoting.Error.CANCELLED);
394 onError(remoting.Error.UNEXPECTED);
398 /** @param {string} pinHash */
399 function updateDaemonConfigWithHash(pinHash) {
401 host_secret_hash: pinHash
403 that.hostDispatcher_.updateDaemonConfig(newConfig, onConfigUpdated,
407 /** @param {Object} config */
408 function onConfig(config) {
409 if (!isHostConfigValid_(config)) {
410 onError(remoting.Error.UNEXPECTED);
413 /** @type {string} */
414 var hostId = config['host_id'];
415 that.hostDispatcher_.getPinHash(hostId, newPin, updateDaemonConfigWithHash,
419 // TODO(sergeyu): When crbug.com/121518 is fixed: replace this call
420 // with an unprivileged version if that is necessary.
421 this.hostDispatcher_.getDaemonConfig(onConfig, onError);
425 * Get the state of the local host.
427 * @param {function(remoting.HostController.State):void} onDone Completion
430 remoting.HostController.prototype.getLocalHostState = function(onDone) {
431 this.hostDispatcher_.getDaemonState(onDone, function(error) {
432 onDone(remoting.HostController.State.UNKNOWN);
437 * Get the id of the local host, or null if it is not registered.
439 * @param {function(string?):void} onDone Completion callback.
441 remoting.HostController.prototype.getLocalHostId = function(onDone) {
442 /** @type {remoting.HostController} */
444 /** @param {Object} config */
445 function onConfig(config) {
447 if (isHostConfigValid_(config)) {
448 hostId = /** @type {string} */ config['host_id'];
453 this.hostDispatcher_.getDaemonConfig(onConfig, function(error) {
459 * Fetch the list of paired clients for this host.
461 * @param {function(Array.<remoting.PairedClient>):void} onDone
462 * @param {function(remoting.Error):void} onError
465 remoting.HostController.prototype.getPairedClients = function(onDone,
467 this.hostDispatcher_.getPairedClients(onDone, onError);
471 * Delete a single paired client.
473 * @param {string} client The client id of the pairing to delete.
474 * @param {function():void} onDone Completion callback.
475 * @param {function(remoting.Error):void} onError Error callback.
478 remoting.HostController.prototype.deletePairedClient = function(
479 client, onDone, onError) {
480 this.hostDispatcher_.deletePairedClient(client, onDone, onError);
484 * Delete all paired clients.
486 * @param {function():void} onDone Completion callback.
487 * @param {function(remoting.Error):void} onError Error callback.
490 remoting.HostController.prototype.clearPairedClients = function(
492 this.hostDispatcher_.clearPairedClients(onDone, onError);
496 * Returns true if the NPAPI plugin is being used.
499 remoting.HostController.prototype.usingNpapiPlugin = function() {
500 return this.hostDispatcher_.usingNpapiPlugin();
503 /** @type {remoting.HostController} */
504 remoting.hostController = null;