Home | History | Annotate | Download | only in plugin
      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 
     30 namespace remoting {
     31 
     32 namespace {
     33 
     34 const char* kAttrNameAccessCode = "accessCode";
     35 const char* kAttrNameAccessCodeLifetime = "accessCodeLifetime";
     36 const char* kAttrNameClient = "client";
     37 const char* kAttrNameDaemonState = "daemonState";
     38 const char* kAttrNameState = "state";
     39 const char* kAttrNameLogDebugInfo = "logDebugInfo";
     40 const char* kAttrNameOnNatTraversalPolicyChanged =
     41     "onNatTraversalPolicyChanged";
     42 const char* kAttrNameOnStateChanged = "onStateChanged";
     43 const char* kAttrNameXmppServerAddress = "xmppServerAddress";
     44 const char* kAttrNameXmppServerUseTls = "xmppServerUseTls";
     45 const char* kAttrNameDirectoryBotJid = "directoryBotJid";
     46 const char* kAttrNameSupportedFeatures = "supportedFeatures";
     47 const char* kFuncNameConnect = "connect";
     48 const char* kFuncNameDisconnect = "disconnect";
     49 const char* kFuncNameLocalize = "localize";
     50 const char* kFuncNameClearPairedClients = "clearPairedClients";
     51 const char* kFuncNameDeletePairedClient = "deletePairedClient";
     52 const char* kFuncNameGetHostName = "getHostName";
     53 const char* kFuncNameGetPinHash = "getPinHash";
     54 const char* kFuncNameGenerateKeyPair = "generateKeyPair";
     55 const char* kFuncNameUpdateDaemonConfig = "updateDaemonConfig";
     56 const char* kFuncNameGetDaemonConfig = "getDaemonConfig";
     57 const char* kFuncNameGetDaemonVersion = "getDaemonVersion";
     58 const char* kFuncNameGetPairedClients = "getPairedClients";
     59 const char* kFuncNameGetUsageStatsConsent = "getUsageStatsConsent";
     60 const char* kFuncNameStartDaemon = "startDaemon";
     61 const char* kFuncNameStopDaemon = "stopDaemon";
     62 
     63 // States.
     64 const char* kAttrNameDisconnected = "DISCONNECTED";
     65 const char* kAttrNameStarting = "STARTING";
     66 const char* kAttrNameRequestedAccessCode = "REQUESTED_ACCESS_CODE";
     67 const char* kAttrNameReceivedAccessCode = "RECEIVED_ACCESS_CODE";
     68 const char* kAttrNameConnected = "CONNECTED";
     69 const char* kAttrNameDisconnecting = "DISCONNECTING";
     70 const char* kAttrNameError = "ERROR";
     71 const char* kAttrNameInvalidDomainError = "INVALID_DOMAIN_ERROR";
     72 
     73 // Space separated list of features supported in addition to the base protocol.
     74 const char* kSupportedFeatures = "pairingRegistry";
     75 
     76 }  // namespace
     77 
     78 HostNPScriptObject::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 
    128 HostNPScriptObject::~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 
    140 bool 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 
    160 bool 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 
    169 bool 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 
    211 bool 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 
    235 bool 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 
    311 bool 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 
    399 bool HostNPScriptObject::RemoveProperty(const std::string& property_name) {
    400   VLOG(2) << "RemoveProperty " << property_name;
    401   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
    402   return false;
    403 }
    404 
    405 bool 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
    446 bool 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 
    499 bool 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 
    516 bool 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 
    535 bool 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 
    561 bool 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 
    594 bool 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 
    615 bool 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 
    649 bool 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 
    674 bool 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 
    714 bool 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 
    735 bool 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 
    757 bool 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 
    784 bool 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 
    805 bool 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 
    846 bool 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 
    869 void 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 
    884 void 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.
    896 void 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.
    905 void HostNPScriptObject::OnClientAuthenticated(
    906     const std::string& client_username) {
    907   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
    908 
    909   client_username_ = client_username;
    910 }
    911 
    912 void 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 
    927 void HostNPScriptObject::SetWindow(NPWindow* np_window) {
    928   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
    929 
    930   daemon_controller_->SetWindow(np_window->window);
    931 }
    932 
    933 void 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 
    945 bool HostNPScriptObject::LocalizeString(NPObject* localize_func,
    946                                         const char* tag,
    947                                         base::string16* result) {
    948   return LocalizeStringWithSubstitution(localize_func, tag, NULL, result);
    949 }
    950 
    951 bool 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
    980 void 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 
    990 void 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 
   1004 void 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 
   1015 void 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 
   1025 void 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 
   1041 void 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 
   1050 void 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 
   1063 void 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 
   1078 void 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 
   1094 bool 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 
   1108 void 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