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#include "remoting/host/plugin/host_script_object.h"
6
7#include "base/bind.h"
8#include "base/json/json_reader.h"
9#include "base/json/json_writer.h"
10#include "base/strings/string_util.h"
11#include "base/strings/stringprintf.h"
12#include "base/strings/sys_string_conversions.h"
13#include "base/strings/utf_string_conversions.h"
14#include "remoting/base/auth_token_util.h"
15#include "remoting/base/auto_thread.h"
16#include "remoting/base/logging.h"
17#include "remoting/base/resources.h"
18#include "remoting/base/rsa_key_pair.h"
19#include "remoting/host/chromoting_host_context.h"
20#include "remoting/host/host_config.h"
21#include "remoting/host/pairing_registry_delegate.h"
22#include "remoting/host/pin_hash.h"
23#include "remoting/host/plugin/host_log_handler.h"
24#include "remoting/host/policy_hack/policy_watcher.h"
25#include "remoting/host/service_urls.h"
26#include "third_party/npapi/bindings/npapi.h"
27#include "third_party/npapi/bindings/npfunctions.h"
28#include "third_party/npapi/bindings/npruntime.h"
29
30namespace remoting {
31
32namespace {
33
34const char* kAttrNameAccessCode = "accessCode";
35const char* kAttrNameAccessCodeLifetime = "accessCodeLifetime";
36const char* kAttrNameClient = "client";
37const char* kAttrNameDaemonState = "daemonState";
38const char* kAttrNameState = "state";
39const char* kAttrNameLogDebugInfo = "logDebugInfo";
40const char* kAttrNameOnNatTraversalPolicyChanged =
41    "onNatTraversalPolicyChanged";
42const char* kAttrNameOnStateChanged = "onStateChanged";
43const char* kAttrNameXmppServerAddress = "xmppServerAddress";
44const char* kAttrNameXmppServerUseTls = "xmppServerUseTls";
45const char* kAttrNameDirectoryBotJid = "directoryBotJid";
46const char* kAttrNameSupportedFeatures = "supportedFeatures";
47const char* kFuncNameConnect = "connect";
48const char* kFuncNameDisconnect = "disconnect";
49const char* kFuncNameLocalize = "localize";
50const char* kFuncNameClearPairedClients = "clearPairedClients";
51const char* kFuncNameDeletePairedClient = "deletePairedClient";
52const char* kFuncNameGetHostName = "getHostName";
53const char* kFuncNameGetPinHash = "getPinHash";
54const char* kFuncNameGenerateKeyPair = "generateKeyPair";
55const char* kFuncNameUpdateDaemonConfig = "updateDaemonConfig";
56const char* kFuncNameGetDaemonConfig = "getDaemonConfig";
57const char* kFuncNameGetDaemonVersion = "getDaemonVersion";
58const char* kFuncNameGetPairedClients = "getPairedClients";
59const char* kFuncNameGetUsageStatsConsent = "getUsageStatsConsent";
60const char* kFuncNameStartDaemon = "startDaemon";
61const char* kFuncNameStopDaemon = "stopDaemon";
62
63// States.
64const char* kAttrNameDisconnected = "DISCONNECTED";
65const char* kAttrNameStarting = "STARTING";
66const char* kAttrNameRequestedAccessCode = "REQUESTED_ACCESS_CODE";
67const char* kAttrNameReceivedAccessCode = "RECEIVED_ACCESS_CODE";
68const char* kAttrNameConnected = "CONNECTED";
69const char* kAttrNameDisconnecting = "DISCONNECTING";
70const char* kAttrNameError = "ERROR";
71const char* kAttrNameInvalidDomainError = "INVALID_DOMAIN_ERROR";
72
73// Space separated list of features supported in addition to the base protocol.
74const char* kSupportedFeatures = "pairingRegistry";
75
76}  // namespace
77
78HostNPScriptObject::HostNPScriptObject(
79    NPP plugin,
80    NPObject* parent,
81    scoped_refptr<AutoThreadTaskRunner> plugin_task_runner)
82    : plugin_(plugin),
83      parent_(parent),
84      plugin_task_runner_(plugin_task_runner),
85      am_currently_logging_(false),
86      state_(kDisconnected),
87      weak_factory_(this) {
88  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
89
90  weak_ptr_ = weak_factory_.GetWeakPtr();
91
92  // Set the thread task runner for the plugin thread so that timers and other
93  // code using |base::ThreadTaskRunnerHandle| could be used on the plugin
94  // thread.
95  //
96  // If component build is used, Chrome and the plugin may end up sharing base
97  // binary. This means that the instance of |base::ThreadTaskRunnerHandle|
98  // created by Chrome for the current thread is shared as well. This routinely
99  // happens in the development setting so the below check for
100  // |!base::ThreadTaskRunnerHandle::IsSet()| is a hack/workaround allowing this
101  // configuration to work. It lets the plugin to access Chrome's message loop
102  // directly via |base::ThreadTaskRunnerHandle|. This is safe as long as both
103  // Chrome and the plugin are built from the same version of the sources.
104  if (!base::ThreadTaskRunnerHandle::IsSet()) {
105    plugin_task_runner_handle_.reset(
106        new base::ThreadTaskRunnerHandle(plugin_task_runner_));
107  }
108
109  daemon_controller_ = DaemonController::Create();
110
111  ServiceUrls* service_urls = ServiceUrls::GetInstance();
112  bool xmpp_server_valid = net::ParseHostAndPort(
113      service_urls->xmpp_server_address(),
114      &xmpp_server_config_.host, &xmpp_server_config_.port);
115  // For the plugin, this is always the default address, which must be valid.
116  DCHECK(xmpp_server_valid);
117  xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
118  directory_bot_jid_ = service_urls->directory_bot_jid();
119
120  // Create worker thread for encryption key generation and loading the paired
121  // clients.
122  worker_thread_ = AutoThread::Create("ChromotingWorkerThread",
123                                      plugin_task_runner_);
124
125  pairing_registry_ = CreatePairingRegistry(worker_thread_);
126}
127
128HostNPScriptObject::~HostNPScriptObject() {
129  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
130
131  HostLogHandler::UnregisterLoggingScriptObject(this);
132
133  // Stop the It2Me host if the caller forgot to.
134  if (it2me_host_.get()) {
135    it2me_host_->Disconnect();
136    it2me_host_ = NULL;
137  }
138}
139
140bool HostNPScriptObject::HasMethod(const std::string& method_name) {
141  VLOG(2) << "HasMethod " << method_name;
142  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
143  return (method_name == kFuncNameConnect ||
144          method_name == kFuncNameDisconnect ||
145          method_name == kFuncNameLocalize ||
146          method_name == kFuncNameClearPairedClients ||
147          method_name == kFuncNameDeletePairedClient ||
148          method_name == kFuncNameGetHostName ||
149          method_name == kFuncNameGetPinHash ||
150          method_name == kFuncNameGenerateKeyPair ||
151          method_name == kFuncNameUpdateDaemonConfig ||
152          method_name == kFuncNameGetDaemonConfig ||
153          method_name == kFuncNameGetDaemonVersion ||
154          method_name == kFuncNameGetPairedClients ||
155          method_name == kFuncNameGetUsageStatsConsent ||
156          method_name == kFuncNameStartDaemon ||
157          method_name == kFuncNameStopDaemon);
158}
159
160bool HostNPScriptObject::InvokeDefault(const NPVariant* args,
161                                       uint32_t arg_count,
162                                       NPVariant* result) {
163  VLOG(2) << "InvokeDefault";
164  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
165  SetException("exception during default invocation");
166  return false;
167}
168
169bool HostNPScriptObject::Invoke(const std::string& method_name,
170                                const NPVariant* args,
171                                uint32_t arg_count,
172                                NPVariant* result) {
173  VLOG(2) << "Invoke " << method_name;
174  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
175  if (method_name == kFuncNameConnect) {
176    return Connect(args, arg_count, result);
177  } else if (method_name == kFuncNameDisconnect) {
178    return Disconnect(args, arg_count, result);
179  } else if (method_name == kFuncNameLocalize) {
180    return Localize(args, arg_count, result);
181  } else if (method_name == kFuncNameClearPairedClients) {
182    return ClearPairedClients(args, arg_count, result);
183  } else if (method_name == kFuncNameDeletePairedClient) {
184    return DeletePairedClient(args, arg_count, result);
185  } else if (method_name == kFuncNameGetHostName) {
186    return GetHostName(args, arg_count, result);
187  } else if (method_name == kFuncNameGetPinHash) {
188    return GetPinHash(args, arg_count, result);
189  } else if (method_name == kFuncNameGenerateKeyPair) {
190    return GenerateKeyPair(args, arg_count, result);
191  } else if (method_name == kFuncNameUpdateDaemonConfig) {
192    return UpdateDaemonConfig(args, arg_count, result);
193  } else if (method_name == kFuncNameGetDaemonConfig) {
194    return GetDaemonConfig(args, arg_count, result);
195  } else if (method_name == kFuncNameGetDaemonVersion) {
196    return GetDaemonVersion(args, arg_count, result);
197  } else if (method_name == kFuncNameGetPairedClients) {
198    return GetPairedClients(args, arg_count, result);
199  } else if (method_name == kFuncNameGetUsageStatsConsent) {
200    return GetUsageStatsConsent(args, arg_count, result);
201  } else if (method_name == kFuncNameStartDaemon) {
202    return StartDaemon(args, arg_count, result);
203  } else if (method_name == kFuncNameStopDaemon) {
204    return StopDaemon(args, arg_count, result);
205  } else {
206    SetException("Invoke: unknown method " + method_name);
207    return false;
208  }
209}
210
211bool HostNPScriptObject::HasProperty(const std::string& property_name) {
212  VLOG(2) << "HasProperty " << property_name;
213  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
214  return (property_name == kAttrNameAccessCode ||
215          property_name == kAttrNameAccessCodeLifetime ||
216          property_name == kAttrNameClient ||
217          property_name == kAttrNameDaemonState ||
218          property_name == kAttrNameState ||
219          property_name == kAttrNameLogDebugInfo ||
220          property_name == kAttrNameOnNatTraversalPolicyChanged ||
221          property_name == kAttrNameOnStateChanged ||
222          property_name == kAttrNameDisconnected ||
223          property_name == kAttrNameStarting ||
224          property_name == kAttrNameRequestedAccessCode ||
225          property_name == kAttrNameReceivedAccessCode ||
226          property_name == kAttrNameConnected ||
227          property_name == kAttrNameDisconnecting ||
228          property_name == kAttrNameError ||
229          property_name == kAttrNameXmppServerAddress ||
230          property_name == kAttrNameXmppServerUseTls ||
231          property_name == kAttrNameDirectoryBotJid ||
232          property_name == kAttrNameSupportedFeatures);
233}
234
235bool HostNPScriptObject::GetProperty(const std::string& property_name,
236                                     NPVariant* result) {
237  VLOG(2) << "GetProperty " << property_name;
238  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
239  if (!result) {
240    SetException("GetProperty: NULL result");
241    return false;
242  }
243
244  if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
245    OBJECT_TO_NPVARIANT(on_nat_traversal_policy_changed_func_.get(), *result);
246    return true;
247  } else if (property_name == kAttrNameOnStateChanged) {
248    OBJECT_TO_NPVARIANT(on_state_changed_func_.get(), *result);
249    return true;
250  } else if (property_name == kAttrNameLogDebugInfo) {
251    OBJECT_TO_NPVARIANT(log_debug_info_func_.get(), *result);
252    return true;
253  } else if (property_name == kAttrNameState) {
254    INT32_TO_NPVARIANT(state_, *result);
255    return true;
256  } else if (property_name == kAttrNameAccessCode) {
257    *result = NPVariantFromString(access_code_);
258    return true;
259  } else if (property_name == kAttrNameAccessCodeLifetime) {
260    INT32_TO_NPVARIANT(access_code_lifetime_.InSeconds(), *result);
261    return true;
262  } else if (property_name == kAttrNameClient) {
263    *result = NPVariantFromString(client_username_);
264    return true;
265  } else if (property_name == kAttrNameDaemonState) {
266    INT32_TO_NPVARIANT(daemon_controller_->GetState(), *result);
267    return true;
268  } else if (property_name == kAttrNameDisconnected) {
269    INT32_TO_NPVARIANT(kDisconnected, *result);
270    return true;
271  } else if (property_name == kAttrNameStarting) {
272    INT32_TO_NPVARIANT(kStarting, *result);
273    return true;
274  } else if (property_name == kAttrNameRequestedAccessCode) {
275    INT32_TO_NPVARIANT(kRequestedAccessCode, *result);
276    return true;
277  } else if (property_name == kAttrNameReceivedAccessCode) {
278    INT32_TO_NPVARIANT(kReceivedAccessCode, *result);
279    return true;
280  } else if (property_name == kAttrNameConnected) {
281    INT32_TO_NPVARIANT(kConnected, *result);
282    return true;
283  } else if (property_name == kAttrNameDisconnecting) {
284    INT32_TO_NPVARIANT(kDisconnecting, *result);
285    return true;
286  } else if (property_name == kAttrNameError) {
287    INT32_TO_NPVARIANT(kError, *result);
288    return true;
289  } else if (property_name == kAttrNameInvalidDomainError) {
290    INT32_TO_NPVARIANT(kInvalidDomainError, *result);
291    return true;
292  } else if (property_name == kAttrNameXmppServerAddress) {
293    *result = NPVariantFromString(base::StringPrintf(
294        "%s:%u", xmpp_server_config_.host.c_str(), xmpp_server_config_.port));
295    return true;
296  } else if (property_name == kAttrNameXmppServerUseTls) {
297    BOOLEAN_TO_NPVARIANT(xmpp_server_config_.use_tls, *result);
298    return true;
299  } else if (property_name == kAttrNameDirectoryBotJid) {
300    *result = NPVariantFromString(directory_bot_jid_);
301    return true;
302  } else if (property_name == kAttrNameSupportedFeatures) {
303    *result = NPVariantFromString(kSupportedFeatures);
304    return true;
305  } else {
306    SetException("GetProperty: unsupported property " + property_name);
307    return false;
308  }
309}
310
311bool HostNPScriptObject::SetProperty(const std::string& property_name,
312                                     const NPVariant* value) {
313  VLOG(2) <<  "SetProperty " << property_name;
314  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
315
316  if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
317    if (NPVARIANT_IS_OBJECT(*value)) {
318      on_nat_traversal_policy_changed_func_ = NPVARIANT_TO_OBJECT(*value);
319      if (it2me_host_.get()) {
320        // Ask the It2Me host to notify the web-app of the policy.
321        it2me_host_->RequestNatPolicy();
322      }
323      return true;
324    } else {
325      SetException("SetProperty: unexpected type for property " +
326                   property_name);
327    }
328    return false;
329  }
330
331  if (property_name == kAttrNameOnStateChanged) {
332    if (NPVARIANT_IS_OBJECT(*value)) {
333      on_state_changed_func_ = NPVARIANT_TO_OBJECT(*value);
334      return true;
335    } else {
336      SetException("SetProperty: unexpected type for property " +
337                   property_name);
338    }
339    return false;
340  }
341
342  if (property_name == kAttrNameLogDebugInfo) {
343    if (NPVARIANT_IS_OBJECT(*value)) {
344      log_debug_info_func_ = NPVARIANT_TO_OBJECT(*value);
345      HostLogHandler::RegisterLoggingScriptObject(this);
346      return true;
347    } else {
348      SetException("SetProperty: unexpected type for property " +
349                   property_name);
350    }
351    return false;
352  }
353
354#if !defined(NDEBUG)
355  if (property_name == kAttrNameXmppServerAddress) {
356    if (NPVARIANT_IS_STRING(*value)) {
357      std::string address = StringFromNPVariant(*value);
358      bool xmpp_server_valid = net::ParseHostAndPort(
359          address, &xmpp_server_config_.host, &xmpp_server_config_.port);
360      if (xmpp_server_valid) {
361        return true;
362      } else {
363        SetException("SetProperty: invalid value for property " +
364                     property_name);
365      }
366    } else {
367      SetException("SetProperty: unexpected type for property " +
368                   property_name);
369    }
370    return false;
371  }
372
373  if (property_name == kAttrNameXmppServerUseTls) {
374    if (NPVARIANT_IS_BOOLEAN(*value)) {
375      xmpp_server_config_.use_tls = NPVARIANT_TO_BOOLEAN(*value);
376      return true;
377    } else {
378      SetException("SetProperty: unexpected type for property " +
379                   property_name);
380    }
381    return false;
382  }
383
384  if (property_name == kAttrNameDirectoryBotJid) {
385    if (NPVARIANT_IS_STRING(*value)) {
386      directory_bot_jid_ = StringFromNPVariant(*value);
387      return true;
388    } else {
389      SetException("SetProperty: unexpected type for property " +
390                   property_name);
391    }
392    return false;
393  }
394#endif  // !defined(NDEBUG)
395
396  return false;
397}
398
399bool HostNPScriptObject::RemoveProperty(const std::string& property_name) {
400  VLOG(2) << "RemoveProperty " << property_name;
401  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
402  return false;
403}
404
405bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) {
406  VLOG(2) << "Enumerate";
407  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
408  const char* entries[] = {
409    kAttrNameAccessCode,
410    kAttrNameState,
411    kAttrNameLogDebugInfo,
412    kAttrNameOnStateChanged,
413    kAttrNameDisconnected,
414    kAttrNameStarting,
415    kAttrNameRequestedAccessCode,
416    kAttrNameReceivedAccessCode,
417    kAttrNameConnected,
418    kAttrNameDisconnecting,
419    kAttrNameError,
420    kAttrNameXmppServerAddress,
421    kAttrNameXmppServerUseTls,
422    kAttrNameDirectoryBotJid,
423    kFuncNameConnect,
424    kFuncNameDisconnect,
425    kFuncNameLocalize,
426    kFuncNameClearPairedClients,
427    kFuncNameDeletePairedClient,
428    kFuncNameGetHostName,
429    kFuncNameGetPinHash,
430    kFuncNameGenerateKeyPair,
431    kFuncNameUpdateDaemonConfig,
432    kFuncNameGetDaemonConfig,
433    kFuncNameGetDaemonVersion,
434    kFuncNameGetPairedClients,
435    kFuncNameGetUsageStatsConsent,
436    kFuncNameStartDaemon,
437    kFuncNameStopDaemon
438  };
439  for (size_t i = 0; i < arraysize(entries); ++i) {
440    values->push_back(entries[i]);
441  }
442  return true;
443}
444
445// string username, string auth_token
446bool HostNPScriptObject::Connect(const NPVariant* args,
447                                 uint32_t arg_count,
448                                 NPVariant* result) {
449  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
450
451  HOST_LOG << "Connecting...";
452
453  if (arg_count != 2) {
454    SetException("connect: bad number of arguments");
455    return false;
456  }
457
458  if (it2me_host_.get()) {
459    SetException("connect: can be called only when disconnected");
460    return false;
461  }
462
463  XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_;
464
465  xmpp_config.username = StringFromNPVariant(args[0]);
466  if (xmpp_config.username.empty()) {
467    SetException("connect: bad username argument");
468    return false;
469  }
470
471  std::string auth_service_with_token = StringFromNPVariant(args[1]);
472  ParseAuthTokenWithService(auth_service_with_token, &xmpp_config.auth_token,
473                            &xmpp_config.auth_service);
474  if (xmpp_config.auth_token.empty()) {
475    SetException("connect: auth_service_with_token argument has empty token");
476    return false;
477  }
478
479  // Create a host context to manage the threads for the it2me host.
480  // The plugin, rather than the It2MeHost object, owns and maintains the
481  // lifetime of the host context.
482  host_context_.reset(
483      ChromotingHostContext::Create(plugin_task_runner_).release());
484  if (!host_context_) {
485    SetException("connect: failed to start threads");
486    return false;
487  }
488
489  // Create the It2Me host and start connecting.
490  scoped_ptr<It2MeHostFactory> factory(new It2MeHostFactory());
491  it2me_host_ = factory->CreateIt2MeHost(
492      host_context_.get(), plugin_task_runner_, weak_ptr_,
493      xmpp_config, directory_bot_jid_);
494  it2me_host_->Connect();
495
496  return true;
497}
498
499bool HostNPScriptObject::Disconnect(const NPVariant* args,
500                                    uint32_t arg_count,
501                                    NPVariant* result) {
502  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
503  if (arg_count != 0) {
504    SetException("disconnect: bad number of arguments");
505    return false;
506  }
507
508  if (it2me_host_.get()) {
509    it2me_host_->Disconnect();
510    it2me_host_ = NULL;
511  }
512
513  return true;
514}
515
516bool HostNPScriptObject::Localize(const NPVariant* args,
517                                  uint32_t arg_count,
518                                  NPVariant* result) {
519  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
520  if (arg_count != 1) {
521    SetException("localize: bad number of arguments");
522    return false;
523  }
524
525  if (NPVARIANT_IS_OBJECT(args[0])) {
526    ScopedRefNPObject localize_func(NPVARIANT_TO_OBJECT(args[0]));
527    LocalizeStrings(localize_func.get());
528    return true;
529  } else {
530    SetException("localize: unexpected type for argument 1");
531    return false;
532  }
533}
534
535bool HostNPScriptObject::ClearPairedClients(const NPVariant* args,
536                                            uint32_t arg_count,
537                                            NPVariant* result) {
538  if (arg_count != 1) {
539    SetException("clearPairedClients: bad number of arguments");
540    return false;
541  }
542
543  if (!NPVARIANT_IS_OBJECT(args[0])) {
544    SetException("clearPairedClients: invalid callback parameter");
545    return false;
546  }
547
548  scoped_ptr<ScopedRefNPObject> callback_obj(
549      new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
550  if (pairing_registry_) {
551    pairing_registry_->ClearAllPairings(
552        base::Bind(&HostNPScriptObject::InvokeBooleanCallback, weak_ptr_,
553                   base::Passed(&callback_obj)));
554  } else {
555    InvokeBooleanCallback(callback_obj.Pass(), false);
556  }
557
558  return true;
559}
560
561bool HostNPScriptObject::DeletePairedClient(const NPVariant* args,
562                                            uint32_t arg_count,
563                                            NPVariant* result) {
564  if (arg_count != 2) {
565    SetException("deletePairedClient: bad number of arguments");
566    return false;
567  }
568
569  if (!NPVARIANT_IS_STRING(args[0])) {
570    SetException("deletePairedClient: bad clientId parameter");
571    return false;
572  }
573
574  if (!NPVARIANT_IS_OBJECT(args[1])) {
575    SetException("deletePairedClient: invalid callback parameter");
576    return false;
577  }
578
579  std::string client_id = StringFromNPVariant(args[0]);
580  scoped_ptr<ScopedRefNPObject> callback_obj(
581      new ScopedRefNPObject(ObjectFromNPVariant(args[1])));
582  if (pairing_registry_) {
583    pairing_registry_->DeletePairing(
584        client_id,
585        base::Bind(&HostNPScriptObject::InvokeBooleanCallback,
586                   weak_ptr_, base::Passed(&callback_obj)));
587  } else {
588    InvokeBooleanCallback(callback_obj.Pass(), false);
589  }
590
591  return true;
592}
593
594bool HostNPScriptObject::GetHostName(const NPVariant* args,
595                                     uint32_t arg_count,
596                                     NPVariant* result) {
597  if (arg_count != 1) {
598    SetException("getHostName: bad number of arguments");
599    return false;
600  }
601
602  ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0]));
603  if (!callback_obj.get()) {
604    SetException("getHostName: invalid callback parameter");
605    return false;
606  }
607
608  NPVariant host_name_val = NPVariantFromString(net::GetHostName());
609  InvokeAndIgnoreResult(callback_obj, &host_name_val, 1);
610  g_npnetscape_funcs->releasevariantvalue(&host_name_val);
611
612  return true;
613}
614
615bool HostNPScriptObject::GetPinHash(const NPVariant* args,
616                                    uint32_t arg_count,
617                                    NPVariant* result) {
618  if (arg_count != 3) {
619    SetException("getPinHash: bad number of arguments");
620    return false;
621  }
622
623  std::string host_id = StringFromNPVariant(args[0]);
624  if (host_id.empty()) {
625    SetException("getPinHash: bad hostId parameter");
626    return false;
627  }
628
629  if (!NPVARIANT_IS_STRING(args[1])) {
630    SetException("getPinHash: bad pin parameter");
631    return false;
632  }
633  std::string pin = StringFromNPVariant(args[1]);
634
635  ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[2]));
636  if (!callback_obj.get()) {
637    SetException("getPinHash: invalid callback parameter");
638    return false;
639  }
640
641  NPVariant pin_hash_val = NPVariantFromString(
642      remoting::MakeHostPinHash(host_id, pin));
643  InvokeAndIgnoreResult(callback_obj, &pin_hash_val, 1);
644  g_npnetscape_funcs->releasevariantvalue(&pin_hash_val);
645
646  return true;
647}
648
649bool HostNPScriptObject::GenerateKeyPair(const NPVariant* args,
650                                         uint32_t arg_count,
651                                         NPVariant* result) {
652  if (arg_count != 1) {
653    SetException("generateKeyPair: bad number of arguments");
654    return false;
655  }
656
657  scoped_ptr<ScopedRefNPObject> callback_obj(
658      new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
659  if (!callback_obj->get()) {
660    SetException("generateKeyPair: invalid callback parameter");
661    return false;
662  }
663
664  base::Callback<void (const std::string&,
665                       const std::string&)> wrapped_callback =
666      base::Bind(&HostNPScriptObject::InvokeGenerateKeyPairCallback, weak_ptr_,
667                 base::Passed(&callback_obj));
668  worker_thread_->PostTask(
669      FROM_HERE, base::Bind(&HostNPScriptObject::DoGenerateKeyPair,
670                            plugin_task_runner_, wrapped_callback));
671  return true;
672}
673
674bool HostNPScriptObject::UpdateDaemonConfig(const NPVariant* args,
675                                            uint32_t arg_count,
676                                            NPVariant* result) {
677  if (arg_count != 2) {
678    SetException("updateDaemonConfig: bad number of arguments");
679    return false;
680  }
681
682  std::string config_str = StringFromNPVariant(args[0]);
683  scoped_ptr<base::Value> config(
684      base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS));
685  if (config_str.empty() || !config.get() ||
686      !config->IsType(base::Value::TYPE_DICTIONARY)) {
687    SetException("updateDaemonConfig: bad config parameter");
688    return false;
689  }
690  scoped_ptr<base::DictionaryValue> config_dict(
691      reinterpret_cast<base::DictionaryValue*>(config.release()));
692
693  scoped_ptr<ScopedRefNPObject> callback_obj(
694      new ScopedRefNPObject(ObjectFromNPVariant(args[1])));
695  if (!callback_obj->get()) {
696    SetException("updateDaemonConfig: invalid callback parameter");
697    return false;
698  }
699
700  if (config_dict->HasKey(kHostIdConfigPath) ||
701      config_dict->HasKey(kXmppLoginConfigPath)) {
702    SetException("updateDaemonConfig: trying to update immutable config "
703                 "parameters");
704    return false;
705  }
706
707  daemon_controller_->UpdateConfig(
708      config_dict.Pass(),
709      base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
710                 base::Passed(&callback_obj)));
711  return true;
712}
713
714bool HostNPScriptObject::GetDaemonConfig(const NPVariant* args,
715                                         uint32_t arg_count,
716                                         NPVariant* result) {
717  if (arg_count != 1) {
718    SetException("getDaemonConfig: bad number of arguments");
719    return false;
720  }
721
722  scoped_ptr<ScopedRefNPObject> callback_obj(
723      new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
724  if (!callback_obj->get()) {
725    SetException("getDaemonConfig: invalid callback parameter");
726    return false;
727  }
728
729  daemon_controller_->GetConfig(
730      base::Bind(&HostNPScriptObject::InvokeGetDaemonConfigCallback, weak_ptr_,
731                 base::Passed(&callback_obj)));
732  return true;
733}
734
735bool HostNPScriptObject::GetDaemonVersion(const NPVariant* args,
736                                          uint32_t arg_count,
737                                          NPVariant* result) {
738  if (arg_count != 1) {
739    SetException("getDaemonVersion: bad number of arguments");
740    return false;
741  }
742
743  scoped_ptr<ScopedRefNPObject> callback_obj(
744      new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
745  if (!callback_obj->get()) {
746    SetException("getDaemonVersion: invalid callback parameter");
747    return false;
748  }
749
750  daemon_controller_->GetVersion(
751      base::Bind(&HostNPScriptObject::InvokeGetDaemonVersionCallback, weak_ptr_,
752                 base::Passed(&callback_obj)));
753
754  return true;
755}
756
757bool HostNPScriptObject::GetPairedClients(const NPVariant* args,
758                                          uint32_t arg_count,
759                                          NPVariant* result) {
760  if (arg_count != 1) {
761    SetException("getPairedClients: bad number of arguments");
762    return false;
763  }
764
765  scoped_ptr<ScopedRefNPObject> callback_obj(
766      new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
767  if (!callback_obj->get()) {
768    SetException("getPairedClients: invalid callback parameter");
769    return false;
770  }
771
772  if (pairing_registry_) {
773    pairing_registry_->GetAllPairings(
774        base::Bind(&HostNPScriptObject::InvokeGetPairedClientsCallback,
775                   weak_ptr_, base::Passed(&callback_obj)));
776  } else {
777    scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
778    InvokeGetPairedClientsCallback(callback_obj.Pass(),
779                                   no_paired_clients.Pass());
780  }
781  return true;
782}
783
784bool HostNPScriptObject::GetUsageStatsConsent(const NPVariant* args,
785                                              uint32_t arg_count,
786                                              NPVariant* result) {
787  if (arg_count != 1) {
788    SetException("getUsageStatsConsent: bad number of arguments");
789    return false;
790  }
791
792  scoped_ptr<ScopedRefNPObject> callback_obj(
793      new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
794  if (!callback_obj->get()) {
795    SetException("getUsageStatsConsent: invalid callback parameter");
796    return false;
797  }
798
799  daemon_controller_->GetUsageStatsConsent(
800      base::Bind(&HostNPScriptObject::InvokeGetUsageStatsConsentCallback,
801                 weak_ptr_, base::Passed(&callback_obj)));
802  return true;
803}
804
805bool HostNPScriptObject::StartDaemon(const NPVariant* args,
806                                     uint32_t arg_count,
807                                     NPVariant* result) {
808  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
809
810  if (arg_count != 3) {
811    SetException("startDaemon: bad number of arguments");
812    return false;
813  }
814
815  std::string config_str = StringFromNPVariant(args[0]);
816  scoped_ptr<base::Value> config(
817      base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS));
818  if (config_str.empty() || !config.get() ||
819      !config->IsType(base::Value::TYPE_DICTIONARY)) {
820    SetException("startDaemon: bad config parameter");
821    return false;
822  }
823  scoped_ptr<base::DictionaryValue> config_dict(
824      reinterpret_cast<base::DictionaryValue*>(config.release()));
825
826  if (!NPVARIANT_IS_BOOLEAN(args[1])) {
827    SetException("startDaemon: invalid consent parameter");
828    return false;
829  }
830
831  scoped_ptr<ScopedRefNPObject> callback_obj(
832      new ScopedRefNPObject(ObjectFromNPVariant(args[2])));
833  if (!callback_obj->get()) {
834    SetException("startDaemon: invalid callback parameter");
835    return false;
836  }
837
838  daemon_controller_->SetConfigAndStart(
839      config_dict.Pass(),
840      NPVARIANT_TO_BOOLEAN(args[1]),
841      base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
842                 base::Passed(&callback_obj)));
843  return true;
844}
845
846bool HostNPScriptObject::StopDaemon(const NPVariant* args,
847                                    uint32_t arg_count,
848                                    NPVariant* result) {
849  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
850
851  if (arg_count != 1) {
852    SetException("stopDaemon: bad number of arguments");
853    return false;
854  }
855
856  scoped_ptr<ScopedRefNPObject> callback_obj(
857      new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
858  if (!callback_obj->get()) {
859    SetException("stopDaemon: invalid callback parameter");
860    return false;
861  }
862
863  daemon_controller_->Stop(
864      base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
865                 base::Passed(&callback_obj)));
866  return true;
867}
868
869void HostNPScriptObject::OnStateChanged(It2MeHostState state) {
870  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
871
872  state_ = state;
873
874  if (state_ == kDisconnected)
875    client_username_.clear();
876
877  if (on_state_changed_func_.get()) {
878    NPVariant state_var;
879    INT32_TO_NPVARIANT(state, state_var);
880    InvokeAndIgnoreResult(on_state_changed_func_, &state_var, 1);
881  }
882}
883
884void HostNPScriptObject::OnNatPolicyChanged(bool nat_traversal_enabled) {
885  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
886
887  if (on_nat_traversal_policy_changed_func_.get()) {
888    NPVariant policy;
889    BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy);
890    InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_,
891                          &policy, 1);
892  }
893}
894
895// Stores the Access Code for the web-app to query.
896void HostNPScriptObject::OnStoreAccessCode(
897    const std::string& access_code, base::TimeDelta access_code_lifetime) {
898  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
899
900  access_code_ = access_code;
901  access_code_lifetime_ = access_code_lifetime;
902}
903
904// Stores the client user's name for the web-app to query.
905void HostNPScriptObject::OnClientAuthenticated(
906    const std::string& client_username) {
907  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
908
909  client_username_ = client_username;
910}
911
912void HostNPScriptObject::PostLogDebugInfo(const std::string& message) {
913  if (plugin_task_runner_->BelongsToCurrentThread()) {
914    // Make sure we're not currently processing a log message.
915    // We only need to check this if we're on the plugin thread.
916    if (am_currently_logging_)
917      return;
918  }
919
920  // Always post (even if we're already on the correct thread) so that debug
921  // log messages are shown in the correct order.
922  plugin_task_runner_->PostTask(
923      FROM_HERE, base::Bind(&HostNPScriptObject::LogDebugInfo,
924                            weak_ptr_, message));
925}
926
927void HostNPScriptObject::SetWindow(NPWindow* np_window) {
928  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
929
930  daemon_controller_->SetWindow(np_window->window);
931}
932
933void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) {
934  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
935
936  // Reload resources for the current locale. The default UI locale is used on
937  // Windows.
938#if !defined(OS_WIN)
939  base::string16 ui_locale;
940  LocalizeString(localize_func, "@@ui_locale", &ui_locale);
941  remoting::LoadResources(UTF16ToUTF8(ui_locale));
942#endif  // !defined(OS_WIN)
943}
944
945bool HostNPScriptObject::LocalizeString(NPObject* localize_func,
946                                        const char* tag,
947                                        base::string16* result) {
948  return LocalizeStringWithSubstitution(localize_func, tag, NULL, result);
949}
950
951bool HostNPScriptObject::LocalizeStringWithSubstitution(
952    NPObject* localize_func,
953    const char* tag,
954    const char* substitution,
955    base::string16* result) {
956  int argc = substitution ? 2 : 1;
957  scoped_ptr<NPVariant[]> args(new NPVariant[argc]);
958  STRINGZ_TO_NPVARIANT(tag, args[0]);
959  if (substitution) {
960    STRINGZ_TO_NPVARIANT(substitution, args[1]);
961  }
962  NPVariant np_result;
963  bool is_good = g_npnetscape_funcs->invokeDefault(
964      plugin_, localize_func, args.get(), argc, &np_result);
965  if (!is_good) {
966    LOG(ERROR) << "Localization failed for " << tag;
967    return false;
968  }
969  std::string translation = StringFromNPVariant(np_result);
970  g_npnetscape_funcs->releasevariantvalue(&np_result);
971  if (translation.empty()) {
972    LOG(ERROR) << "Missing translation for " << tag;
973    return false;
974  }
975  *result = UTF8ToUTF16(translation);
976  return true;
977}
978
979// static
980void HostNPScriptObject::DoGenerateKeyPair(
981    const scoped_refptr<AutoThreadTaskRunner>& plugin_task_runner,
982    const base::Callback<void (const std::string&,
983                               const std::string&)>& callback) {
984  scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
985  plugin_task_runner->PostTask(FROM_HERE,
986                               base::Bind(callback, key_pair->ToString(),
987                                          key_pair->GetPublicKey()));
988}
989
990void HostNPScriptObject::InvokeGenerateKeyPairCallback(
991    scoped_ptr<ScopedRefNPObject> callback,
992    const std::string& private_key,
993    const std::string& public_key) {
994  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
995
996  NPVariant params[2];
997  params[0] = NPVariantFromString(private_key);
998  params[1] = NPVariantFromString(public_key);
999  InvokeAndIgnoreResult(*callback, params, arraysize(params));
1000  g_npnetscape_funcs->releasevariantvalue(&(params[0]));
1001  g_npnetscape_funcs->releasevariantvalue(&(params[1]));
1002}
1003
1004void HostNPScriptObject::InvokeAsyncResultCallback(
1005    scoped_ptr<ScopedRefNPObject> callback,
1006    DaemonController::AsyncResult result) {
1007  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1008
1009  NPVariant result_var;
1010  INT32_TO_NPVARIANT(static_cast<int32>(result), result_var);
1011  InvokeAndIgnoreResult(*callback, &result_var, 1);
1012  g_npnetscape_funcs->releasevariantvalue(&result_var);
1013}
1014
1015void HostNPScriptObject::InvokeBooleanCallback(
1016    scoped_ptr<ScopedRefNPObject> callback, bool result) {
1017  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1018
1019  NPVariant result_var;
1020  BOOLEAN_TO_NPVARIANT(result, result_var);
1021  InvokeAndIgnoreResult(*callback, &result_var, 1);
1022  g_npnetscape_funcs->releasevariantvalue(&result_var);
1023}
1024
1025void HostNPScriptObject::InvokeGetDaemonConfigCallback(
1026    scoped_ptr<ScopedRefNPObject> callback,
1027    scoped_ptr<base::DictionaryValue> config) {
1028  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1029
1030  // There is no easy way to create a dictionary from an NPAPI plugin
1031  // so we have to serialize the dictionary to pass it to JavaScript.
1032  std::string config_str;
1033  if (config.get())
1034    base::JSONWriter::Write(config.get(), &config_str);
1035
1036  NPVariant config_val = NPVariantFromString(config_str);
1037  InvokeAndIgnoreResult(*callback, &config_val, 1);
1038  g_npnetscape_funcs->releasevariantvalue(&config_val);
1039}
1040
1041void HostNPScriptObject::InvokeGetDaemonVersionCallback(
1042    scoped_ptr<ScopedRefNPObject> callback, const std::string& version) {
1043  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1044
1045  NPVariant version_val = NPVariantFromString(version);
1046  InvokeAndIgnoreResult(*callback, &version_val, 1);
1047  g_npnetscape_funcs->releasevariantvalue(&version_val);
1048}
1049
1050void HostNPScriptObject::InvokeGetPairedClientsCallback(
1051    scoped_ptr<ScopedRefNPObject> callback,
1052    scoped_ptr<base::ListValue> paired_clients) {
1053  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1054
1055  std::string paired_clients_json;
1056  base::JSONWriter::Write(paired_clients.get(), &paired_clients_json);
1057
1058  NPVariant paired_clients_val = NPVariantFromString(paired_clients_json);
1059  InvokeAndIgnoreResult(*callback, &paired_clients_val, 1);
1060  g_npnetscape_funcs->releasevariantvalue(&paired_clients_val);
1061}
1062
1063void HostNPScriptObject::InvokeGetUsageStatsConsentCallback(
1064    scoped_ptr<ScopedRefNPObject> callback,
1065    const DaemonController::UsageStatsConsent& consent) {
1066  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1067
1068  NPVariant params[3];
1069  BOOLEAN_TO_NPVARIANT(consent.supported, params[0]);
1070  BOOLEAN_TO_NPVARIANT(consent.allowed, params[1]);
1071  BOOLEAN_TO_NPVARIANT(consent.set_by_policy, params[2]);
1072  InvokeAndIgnoreResult(*callback, params, arraysize(params));
1073  g_npnetscape_funcs->releasevariantvalue(&(params[0]));
1074  g_npnetscape_funcs->releasevariantvalue(&(params[1]));
1075  g_npnetscape_funcs->releasevariantvalue(&(params[2]));
1076}
1077
1078void HostNPScriptObject::LogDebugInfo(const std::string& message) {
1079  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1080
1081  if (log_debug_info_func_.get()) {
1082    am_currently_logging_ = true;
1083    NPVariant log_message;
1084    STRINGZ_TO_NPVARIANT(message.c_str(), log_message);
1085    bool is_good = InvokeAndIgnoreResult(log_debug_info_func_,
1086                                         &log_message, 1);
1087    if (!is_good) {
1088      LOG(ERROR) << "ERROR - LogDebugInfo failed\n";
1089    }
1090    am_currently_logging_ = false;
1091  }
1092}
1093
1094bool HostNPScriptObject::InvokeAndIgnoreResult(const ScopedRefNPObject& func,
1095                                               const NPVariant* args,
1096                                               uint32_t arg_count) {
1097  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1098
1099  NPVariant np_result;
1100  bool is_good = g_npnetscape_funcs->invokeDefault(plugin_, func.get(), args,
1101                                                   arg_count, &np_result);
1102  if (is_good)
1103      g_npnetscape_funcs->releasevariantvalue(&np_result);
1104
1105  return is_good;
1106}
1107
1108void HostNPScriptObject::SetException(const std::string& exception_string) {
1109  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1110
1111  g_npnetscape_funcs->setexception(parent_, exception_string.c_str());
1112  HOST_LOG << exception_string;
1113}
1114
1115}  // namespace remoting
1116