Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / remoting / webapp / host_controller.js
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.
4
5 'use strict';
6
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
9
10 /** @constructor */
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);
17   };
18
19   /** @type {remoting.HostDispatcher} @private */
20   this.hostDispatcher_ = new remoting.HostDispatcher(createPluginForMe2Me);
21
22   /** @param {string} version */
23   var printVersion = function(version) {
24     if (version == '') {
25       console.log('Host not installed.');
26     } else {
27       console.log('Host version: ' + version);
28     }
29   };
30
31   this.hostDispatcher_.getDaemonVersion(printVersion, function() {
32     console.log('Host version not available.');
33   });
34 };
35
36 // Note that the values in the enums below are copied from
37 // daemon_controller.h and must be kept in sync.
38 /** @enum {number} */
39 remoting.HostController.State = {
40   NOT_IMPLEMENTED: -1,
41   NOT_INSTALLED: 0,
42   INSTALLING: 1,
43   STOPPED: 2,
44   STARTING: 3,
45   STARTED: 4,
46   STOPPING: 5,
47   UNKNOWN: 6
48 };
49
50 /**
51  * @param {string} state The host controller state name.
52  * @return {remoting.HostController.State} The state enum value.
53  */
54 remoting.HostController.State.fromString = function(state) {
55   if (!remoting.HostController.State.hasOwnProperty(state)) {
56     throw "Invalid HostController.State: " + state;
57   }
58   return remoting.HostController.State[state];
59 }
60
61 /** @enum {number} */
62 remoting.HostController.AsyncResult = {
63   OK: 0,
64   FAILED: 1,
65   CANCELLED: 2,
66   FAILED_DIRECTORY: 3
67 };
68
69 /**
70  * @param {string} result The async result name.
71  * @return {remoting.HostController.AsyncResult} The result enum value.
72  */
73 remoting.HostController.AsyncResult.fromString = function(result) {
74   if (!remoting.HostController.AsyncResult.hasOwnProperty(result)) {
75     throw "Invalid HostController.AsyncResult: " + result;
76   }
77   return remoting.HostController.AsyncResult[result];
78 }
79
80 /**
81  * Set of features for which hasFeature() can be used to test.
82  *
83  * @enum {string}
84  */
85 remoting.HostController.Feature = {
86   PAIRING_REGISTRY: 'pairingRegistry',
87   OAUTH_CLIENT: 'oauthClient'
88 };
89
90 /**
91  * @param {remoting.HostController.Feature} feature The feature to test for.
92  * @param {function(boolean):void} callback
93  * @return {void}
94  */
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);
99 };
100
101 /**
102  * @param {function(boolean, boolean, boolean):void} onDone Callback to be
103  *     called when done.
104  * @param {function(remoting.Error):void} onError Callback to be called on
105  *     error.
106  */
107 remoting.HostController.prototype.getConsent = function(onDone, onError) {
108   this.hostDispatcher_.getUsageStatsConsent(onDone, onError);
109 };
110
111 /**
112  * Registers and starts the host.
113  *
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
118  *     error.
119  * @return {void} Nothing.
120  */
121 remoting.HostController.prototype.start = function(hostPin, consent, onDone,
122                                                    onError) {
123   /** @type {remoting.HostController} */
124   var that = this;
125
126   /** @return {string} */
127   function generateUuid() {
128     var random = new Uint16Array(8);
129     window.crypto.getRandomValues(random);
130     /** @type {Array.<string>} */
131     var e = new Array();
132     for (var i = 0; i < 8; i++) {
133       e[i] = (/** @type {number} */random[i] + 0x10000).
134           toString(16).substring(1);
135     }
136     return e[0] + e[1] + '-' + e[2] + '-' + e[3] + '-' +
137         e[4] + '-' + e[5] + e[6] + e[7];
138   };
139
140   var newHostId = generateUuid();
141
142   /** @param {remoting.Error} error */
143   function onStartError(error) {
144     // Unregister the host if we failed to start it.
145     remoting.HostList.unregisterHostById(newHostId);
146     onError(error);
147   }
148
149   /**
150    * @param {string} hostName
151    * @param {string} publicKey
152    * @param {remoting.HostController.AsyncResult} result
153    */
154   function onStarted(hostName, publicKey, result) {
155     if (result == remoting.HostController.AsyncResult.OK) {
156       remoting.hostList.onLocalHostStarted(hostName, newHostId, publicKey);
157       onDone();
158     } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
159       onStartError(remoting.Error.CANCELLED);
160     } else {
161       onStartError(remoting.Error.UNEXPECTED);
162     }
163   }
164
165   /**
166    * @param {string} hostName
167    * @param {string} publicKey
168    * @param {string} privateKey
169    * @param {string} xmppLogin
170    * @param {string} refreshToken
171    * @param {string} hostSecretHash
172    */
173   function startHostWithHash(hostName, publicKey, privateKey,
174                              xmppLogin, refreshToken, hostSecretHash) {
175     var hostConfig = {
176       xmpp_login: xmppLogin,
177       oauth_refresh_token: refreshToken,
178       host_id: newHostId,
179       host_name: hostName,
180       host_secret_hash: hostSecretHash,
181       private_key: privateKey
182     };
183     var hostOwner = remoting.identity.getCachedEmail();
184     if (hostOwner != xmppLogin) {
185       hostConfig['host_owner'] = hostOwner;
186     }
187     that.hostDispatcher_.startDaemon(hostConfig, consent,
188                                      onStarted.bind(null, hostName, publicKey),
189                                      onStartError);
190   }
191
192   /**
193    * @param {string} hostName
194    * @param {string} publicKey
195    * @param {string} privateKey
196    * @param {string} email
197    * @param {string} refreshToken
198    */
199   function onServiceAccountCredentials(
200       hostName, publicKey, privateKey, email, refreshToken) {
201     that.hostDispatcher_.getPinHash(
202         newHostId, hostPin,
203         startHostWithHash.bind(
204             null, hostName, publicKey, privateKey, email, refreshToken),
205         onError);
206   }
207
208   /**
209    * @param {string} hostName
210    * @param {string} publicKey
211    * @param {string} privateKey
212    * @param {XMLHttpRequest} xhr
213    */
214   function onRegistered(
215       hostName, publicKey, privateKey, xhr) {
216     var success = (xhr.status == 200);
217
218     if (success) {
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),
225             onError);
226       } else {
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()),
233           onError);
234       }
235     } else {
236       console.log('Failed to register the host. Status: ' + xhr.status +
237                   ' response: ' + xhr.responseText);
238       onError(remoting.Error.REGISTRATION_FAILED);
239     }
240   }
241
242   /**
243    * @param {string} hostName
244    * @param {string} privateKey
245    * @param {string} publicKey
246    * @param {string} hostClientId
247    * @param {string} oauthToken
248    */
249   function doRegisterHost(
250       hostName, privateKey, publicKey, hostClientId, oauthToken) {
251     var headers = {
252       'Authorization': 'OAuth ' + oauthToken,
253       'Content-type' : 'application/json; charset=UTF-8'
254     };
255
256     var newHostDetails = { data: {
257        hostId: newHostId,
258        hostName: hostName,
259        publicKey: publicKey
260     } };
261
262     var registerHostUrl =
263         remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts';
264
265     if (hostClientId) {
266       registerHostUrl += '?' + remoting.xhr.urlencodeParamHash(
267           { hostClientId: hostClientId });
268     }
269
270     remoting.xhr.post(
271         registerHostUrl,
272         onRegistered.bind(null, hostName, publicKey, privateKey),
273         JSON.stringify(newHostDetails),
274         headers);
275   }
276
277   /**
278    * @param {string} hostName
279    * @param {string} privateKey
280    * @param {string} publicKey
281    * @param {string} hostClientId
282    */
283   function onHostClientId(
284       hostName, privateKey, publicKey, hostClientId) {
285     remoting.identity.callWithToken(
286         doRegisterHost.bind(
287             null, hostName, privateKey, publicKey, hostClientId), onError);
288   }
289
290   /**
291    * @param {string} hostName
292    * @param {string} privateKey
293    * @param {string} publicKey
294    * @param {boolean} hasFeature
295    */
296   function onHasFeatureOAuthClient(
297       hostName, privateKey, publicKey, hasFeature) {
298     if (hasFeature) {
299       that.hostDispatcher_.getHostClientId(
300           onHostClientId.bind(null, hostName, privateKey, publicKey), onError);
301     } else {
302       remoting.identity.callWithToken(
303           doRegisterHost.bind(
304               null, hostName, privateKey, publicKey, null), onError);
305     }
306   }
307
308   /**
309    * @param {string} hostName
310    * @param {string} privateKey
311    * @param {string} publicKey
312    */
313   function onKeyGenerated(hostName, privateKey, publicKey) {
314     that.hasFeature(
315         remoting.HostController.Feature.OAUTH_CLIENT,
316         onHasFeatureOAuthClient.bind(null, hostName, privateKey, publicKey));
317   }
318
319   /**
320    * @param {string} hostName
321    * @return {void} Nothing.
322    */
323   function startWithHostname(hostName) {
324     that.hostDispatcher_.generateKeyPair(onKeyGenerated.bind(null, hostName),
325                                          onError);
326   }
327
328   this.hostDispatcher_.getHostName(startWithHostname, onError);
329 };
330
331 /**
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
335  *     error.
336  * @return {void} Nothing.
337  */
338 remoting.HostController.prototype.stop = function(onDone, onError) {
339   /** @type {remoting.HostController} */
340   var that = this;
341
342   /** @param {string?} hostId The host id of the local host. */
343   function unregisterHost(hostId) {
344     if (hostId) {
345       remoting.HostList.unregisterHostById(hostId);
346     }
347     onDone();
348   }
349
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);
356     } else {
357       onError(remoting.Error.UNEXPECTED);
358     }
359   }
360
361   this.hostDispatcher_.stopDaemon(onStopped, onError);
362 };
363
364 /**
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.
369  */
370 function isHostConfigValid_(config) {
371   return !!config && typeof config['host_id'] == 'string' &&
372       typeof config['xmpp_login'] == 'string';
373 }
374
375 /**
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
379  *     error.
380  * @return {void} Nothing.
381  */
382 remoting.HostController.prototype.updatePin = function(newPin, onDone,
383                                                        onError) {
384   /** @type {remoting.HostController} */
385   var that = this;
386
387   /** @param {remoting.HostController.AsyncResult} result */
388   function onConfigUpdated(result) {
389     if (result == remoting.HostController.AsyncResult.OK) {
390       onDone();
391     } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
392       onError(remoting.Error.CANCELLED);
393     } else {
394       onError(remoting.Error.UNEXPECTED);
395     }
396   }
397
398   /** @param {string} pinHash */
399   function updateDaemonConfigWithHash(pinHash) {
400     var newConfig = {
401       host_secret_hash: pinHash
402     };
403     that.hostDispatcher_.updateDaemonConfig(newConfig, onConfigUpdated,
404                                             onError);
405   }
406
407   /** @param {Object} config */
408   function onConfig(config) {
409     if (!isHostConfigValid_(config)) {
410       onError(remoting.Error.UNEXPECTED);
411       return;
412     }
413     /** @type {string} */
414     var hostId = config['host_id'];
415     that.hostDispatcher_.getPinHash(hostId, newPin, updateDaemonConfigWithHash,
416                                     onError);
417   }
418
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);
422 };
423
424 /**
425  * Get the state of the local host.
426  *
427  * @param {function(remoting.HostController.State):void} onDone Completion
428  *     callback.
429  */
430 remoting.HostController.prototype.getLocalHostState = function(onDone) {
431   this.hostDispatcher_.getDaemonState(onDone, function(error) {
432     onDone(remoting.HostController.State.UNKNOWN);
433   });
434 };
435
436 /**
437  * Get the id of the local host, or null if it is not registered.
438  *
439  * @param {function(string?):void} onDone Completion callback.
440  */
441 remoting.HostController.prototype.getLocalHostId = function(onDone) {
442   /** @type {remoting.HostController} */
443   var that = this;
444   /** @param {Object} config */
445   function onConfig(config) {
446     var hostId = null;
447     if (isHostConfigValid_(config)) {
448       hostId = /** @type {string} */ config['host_id'];
449     }
450     onDone(hostId);
451   };
452
453   this.hostDispatcher_.getDaemonConfig(onConfig, function(error) {
454     onDone(null);
455   });
456 };
457
458 /**
459  * Fetch the list of paired clients for this host.
460  *
461  * @param {function(Array.<remoting.PairedClient>):void} onDone
462  * @param {function(remoting.Error):void} onError
463  * @return {void}
464  */
465 remoting.HostController.prototype.getPairedClients = function(onDone,
466                                                               onError) {
467   this.hostDispatcher_.getPairedClients(onDone, onError);
468 };
469
470 /**
471  * Delete a single paired client.
472  *
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.
476  * @return {void}
477  */
478 remoting.HostController.prototype.deletePairedClient = function(
479     client, onDone, onError) {
480   this.hostDispatcher_.deletePairedClient(client, onDone, onError);
481 };
482
483 /**
484  * Delete all paired clients.
485  *
486  * @param {function():void} onDone Completion callback.
487  * @param {function(remoting.Error):void} onError Error callback.
488  * @return {void}
489  */
490 remoting.HostController.prototype.clearPairedClients = function(
491     onDone, onError) {
492   this.hostDispatcher_.clearPairedClients(onDone, onError);
493 };
494
495 /**
496  * Returns true if the NPAPI plugin is being used.
497  * @return {boolean}
498  */
499 remoting.HostController.prototype.usingNpapiPlugin = function() {
500   return this.hostDispatcher_.usingNpapiPlugin();
501 }
502
503 /** @type {remoting.HostController} */
504 remoting.hostController = null;