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;