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 /** @type {remoting.HostPlugin} @private */
13 this.plugin_
= remoting
.HostSession
.createPlugin();
14 /** @type {HTMLElement} @private */
15 this.container_
= document
.getElementById('daemon-plugin-container');
16 this.container_
.appendChild(this.plugin_
);
17 /** @type {string?} */
18 this.localHostId_
= null;
19 /** @param {string} version */
20 var printVersion = function(version
) {
22 console
.log('Host not installed.');
24 console
.log('Host version: ' + version
);
28 this.plugin_
.getDaemonVersion(printVersion
);
30 console
.log('Host version not available.');
34 // Note that the values in the enums below are copied from
35 // daemon_controller.h and must be kept in sync.
37 remoting
.HostController
.State
= {
49 remoting
.HostController
.AsyncResult
= {
56 /** @return {remoting.HostController.State} The current state of the daemon. */
57 remoting
.HostController
.prototype.state = function() {
58 var result
= this.plugin_
.daemonState
;
59 if (typeof(result
) == 'undefined') {
60 // If the plug-in can't be instantiated, for example on ChromeOS, then
61 // return something sensible.
62 return remoting
.HostController
.State
.NOT_IMPLEMENTED
;
69 * @param {function(boolean, boolean, boolean):void} callback Callback to be
72 remoting
.HostController
.prototype.getConsent = function(callback
) {
73 this.plugin_
.getUsageStatsConsent(callback
);
77 * Registers and starts the host.
78 * @param {string} hostPin Host PIN.
79 * @param {boolean} consent The user's consent to crash dump reporting.
80 * @param {function(remoting.HostController.AsyncResult):void} callback
81 * callback Callback to be called when done.
82 * @return {void} Nothing.
84 remoting
.HostController
.prototype.start = function(hostPin
, consent
, callback
) {
85 /** @type {remoting.HostController} */
87 var hostName
= this.plugin_
.getHostName();
89 /** @return {string} */
90 function generateUuid() {
91 var random
= new Uint16Array(8);
92 window
.crypto
.getRandomValues(random
);
93 /** @type {Array.<string>} */
95 for (var i
= 0; i
< 8; i
++) {
96 e
[i
] = (/** @type {number} */random
[i
] + 0x10000).
97 toString(16).substring(1);
99 return e
[0] + e
[1] + '-' + e
[2] + "-" + e
[3] + '-' +
100 e
[4] + '-' + e
[5] + e
[6] + e
[7];
103 var newHostId
= generateUuid();
105 /** @param {function(remoting.HostController.AsyncResult):void} callback
106 * @param {remoting.HostController.AsyncResult} result
107 * @param {string} hostName
108 * @param {string} publicKey */
109 function onStarted(callback
, result
, hostName
, publicKey
) {
110 if (result
== remoting
.HostController
.AsyncResult
.OK
) {
111 that
.localHostId_
= newHostId
;
112 remoting
.hostList
.onLocalHostStarted(hostName
, newHostId
, publicKey
);
114 that
.localHostId_
= null;
115 // Unregister the host if we failed to start it.
116 remoting
.HostList
.unregisterHostById(newHostId
);
121 /** @param {string} publicKey
122 * @param {string} privateKey
123 * @param {XMLHttpRequest} xhr */
124 function onRegistered(publicKey
, privateKey
, xhr
) {
125 var success
= (xhr
.status
== 200);
129 that
.plugin_
.getPinHash(newHostId
, hostPin
);
130 var hostConfig
= JSON
.stringify({
131 xmpp_login
: remoting
.oauth2
.getCachedEmail(),
132 oauth_refresh_token
: remoting
.oauth2
.exportRefreshToken(),
135 host_secret_hash
: hostSecretHash
,
136 private_key
: privateKey
138 /** @param {remoting.HostController.AsyncResult} result */
139 var onStartDaemon = function(result
) {
140 onStarted(callback
, result
, hostName
, publicKey
);
142 that
.plugin_
.startDaemon(hostConfig
, consent
, onStartDaemon
);
144 console
.log('Failed to register the host. Status: ' + xhr
.status
+
145 ' response: ' + xhr
.responseText
);
146 callback(remoting
.HostController
.AsyncResult
.FAILED_DIRECTORY
);
151 * @param {string} privateKey
152 * @param {string} publicKey
153 * @param {string} oauthToken
155 function doRegisterHost(privateKey
, publicKey
, oauthToken
) {
157 'Authorization': 'OAuth ' + oauthToken
,
158 'Content-type' : 'application/json; charset=UTF-8'
161 var newHostDetails
= { data
: {
167 'https://www.googleapis.com/chromoting/v1/@me/hosts/',
168 /** @param {XMLHttpRequest} xhr */
169 function (xhr
) { onRegistered(publicKey
, privateKey
, xhr
); },
170 JSON
.stringify(newHostDetails
),
174 /** @param {string} privateKey
175 * @param {string} publicKey */
176 function onKeyGenerated(privateKey
, publicKey
) {
177 remoting
.oauth2
.callWithToken(
178 /** @param {string} oauthToken */
179 function(oauthToken
) {
180 doRegisterHost(privateKey
, publicKey
, oauthToken
);
182 /** @param {remoting.Error} error */
184 // TODO(jamiewalch): Have a more specific error code here?
185 callback(remoting
.HostController
.AsyncResult
.FAILED
);
189 this.plugin_
.generateKeyPair(onKeyGenerated
);
193 * Stop the daemon process.
194 * @param {function(remoting.HostController.AsyncResult):void} callback
195 * Callback to be called when finished.
196 * @return {void} Nothing.
198 remoting
.HostController
.prototype.stop = function(callback
) {
199 /** @type {remoting.HostController} */
202 /** @param {remoting.HostController.AsyncResult} result */
203 function onStopped(result
) {
204 if (result
== remoting
.HostController
.AsyncResult
.OK
&&
206 remoting
.HostList
.unregisterHostById(that
.localHostId_
);
210 this.plugin_
.stopDaemon(onStopped
);
214 * Parse a stringified host configuration and return it as a dictionary if it
215 * is well-formed and contains both host_id and xmpp_login keys. null is
216 * returned if either key is missing, or if the configuration is corrupt.
217 * @param {string} configStr The host configuration, JSON encoded to a string.
218 * @return {Object.<string,string>|null} The host configuration.
220 function parseHostConfig_(configStr
) {
221 var config
= /** @type {Object.<string,string>} */ jsonParseSafe(configStr
);
223 typeof config
['host_id'] == 'string' &&
224 typeof config
['xmpp_login'] == 'string') {
227 // {} means that host is not configured; '' means that the config file could
229 // TODO(jamiewalch): '' is expected if the host isn't installed, but should
230 // be reported as an error otherwise. Fix this once we have an event-based
231 // daemon state mechanism.
232 if (configStr
!= '{}' && configStr
!= '') {
233 console
.error('Invalid getDaemonConfig response.');
240 * @param {string} newPin The new PIN to set
241 * @param {function(remoting.HostController.AsyncResult):void} callback
242 * Callback to be called when finished.
243 * @return {void} Nothing.
245 remoting
.HostController
.prototype.updatePin = function(newPin
, callback
) {
246 /** @type {remoting.HostController} */
249 /** @param {string} configStr */
250 function onConfig(configStr
) {
251 var config
= parseHostConfig_(configStr
);
253 callback(remoting
.HostController
.AsyncResult
.FAILED
);
256 var hostId
= config
['host_id'];
257 var newConfig
= JSON
.stringify({
258 host_secret_hash
: that
.plugin_
.getPinHash(hostId
, newPin
)
260 that
.plugin_
.updateDaemonConfig(newConfig
, callback
);
263 // TODO(sergeyu): When crbug.com/121518 is fixed: replace this call
264 // with an upriveleged version if that is necessary.
265 this.plugin_
.getDaemonConfig(onConfig
);
269 * Update the internal state so that the local host can be correctly filtered
270 * out of the host list.
272 * @param {function(string?):void} onDone Completion callback.
274 remoting
.HostController
.prototype.getLocalHostId = function(onDone
) {
275 /** @type {remoting.HostController} */
277 /** @param {string} configStr */
278 function onConfig(configStr
) {
279 var config
= parseHostConfig_(configStr
);
281 that
.localHostId_
= config
['host_id'];
282 onDone(that
.localHostId_
);
288 this.plugin_
.getDaemonConfig(onConfig
);
294 /** @type {remoting.HostController} */
295 remoting
.hostController
= null;