Home | History | Annotate | Download | only in host
      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/register_support_host_request.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/time/time.h"
     12 #include "remoting/base/constants.h"
     13 #include "remoting/host/host_config.h"
     14 #include "remoting/jingle_glue/iq_sender.h"
     15 #include "remoting/jingle_glue/signal_strategy.h"
     16 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
     17 #include "third_party/libjingle/source/talk/xmpp/constants.h"
     18 
     19 using buzz::QName;
     20 using buzz::XmlElement;
     21 
     22 namespace remoting {
     23 
     24 namespace {
     25 // Strings used in the request message we send to the bot.
     26 const char kRegisterQueryTag[] = "register-support-host";
     27 const char kPublicKeyTag[] = "public-key";
     28 const char kSignatureTag[] = "signature";
     29 const char kSignatureTimeAttr[] = "time";
     30 
     31 // Strings used to parse responses received from the bot.
     32 const char kRegisterQueryResultTag[] = "register-support-host-result";
     33 const char kSupportIdTag[] = "support-id";
     34 const char kSupportIdLifetimeTag[] = "support-id-lifetime";
     35 }
     36 
     37 RegisterSupportHostRequest::RegisterSupportHostRequest(
     38     SignalStrategy* signal_strategy,
     39     scoped_refptr<RsaKeyPair> key_pair,
     40     const std::string& directory_bot_jid,
     41     const RegisterCallback& callback)
     42     : signal_strategy_(signal_strategy),
     43       key_pair_(key_pair),
     44       directory_bot_jid_(directory_bot_jid),
     45       callback_(callback) {
     46   DCHECK(signal_strategy_);
     47   DCHECK(key_pair_.get());
     48   signal_strategy_->AddListener(this);
     49   iq_sender_.reset(new IqSender(signal_strategy_));
     50 }
     51 
     52 RegisterSupportHostRequest::~RegisterSupportHostRequest() {
     53   if (signal_strategy_)
     54     signal_strategy_->RemoveListener(this);
     55 }
     56 
     57 void RegisterSupportHostRequest::OnSignalStrategyStateChange(
     58     SignalStrategy::State state) {
     59   if (state == SignalStrategy::CONNECTED) {
     60     DCHECK(!callback_.is_null());
     61 
     62     request_ = iq_sender_->SendIq(
     63         buzz::STR_SET, directory_bot_jid_,
     64         CreateRegistrationRequest(signal_strategy_->GetLocalJid()).Pass(),
     65         base::Bind(&RegisterSupportHostRequest::ProcessResponse,
     66                    base::Unretained(this)));
     67   } else if (state == SignalStrategy::DISCONNECTED) {
     68     // We will reach here if signaling fails to connect.
     69     CallCallback(false, std::string(), base::TimeDelta());
     70   }
     71 }
     72 
     73 bool RegisterSupportHostRequest::OnSignalStrategyIncomingStanza(
     74     const buzz::XmlElement* stanza) {
     75   return false;
     76 }
     77 
     78 scoped_ptr<XmlElement> RegisterSupportHostRequest::CreateRegistrationRequest(
     79     const std::string& jid) {
     80   scoped_ptr<XmlElement> query(new XmlElement(
     81       QName(kChromotingXmlNamespace, kRegisterQueryTag)));
     82   XmlElement* public_key = new XmlElement(
     83       QName(kChromotingXmlNamespace, kPublicKeyTag));
     84   public_key->AddText(key_pair_->GetPublicKey());
     85   query->AddElement(public_key);
     86   query->AddElement(CreateSignature(jid).release());
     87   return query.Pass();
     88 }
     89 
     90 scoped_ptr<XmlElement> RegisterSupportHostRequest::CreateSignature(
     91     const std::string& jid) {
     92   scoped_ptr<XmlElement> signature_tag(new XmlElement(
     93       QName(kChromotingXmlNamespace, kSignatureTag)));
     94 
     95   int64 time = static_cast<int64>(base::Time::Now().ToDoubleT());
     96   std::string time_str(base::Int64ToString(time));
     97   signature_tag->AddAttr(
     98       QName(kChromotingXmlNamespace, kSignatureTimeAttr), time_str);
     99 
    100   std::string message = jid + ' ' + time_str;
    101   std::string signature(key_pair_->SignMessage(message));
    102   signature_tag->AddText(signature);
    103 
    104   return signature_tag.Pass();
    105 }
    106 
    107 bool RegisterSupportHostRequest::ParseResponse(const XmlElement* response,
    108                                                std::string* support_id,
    109                                                base::TimeDelta* lifetime) {
    110   std::string type = response->Attr(buzz::QN_TYPE);
    111   if (type == buzz::STR_ERROR) {
    112     LOG(ERROR) << "Received error in response to heartbeat: "
    113                << response->Str();
    114     return false;
    115   }
    116 
    117   // This method must only be called for error or result stanzas.
    118   if (type != buzz::STR_RESULT) {
    119     LOG(ERROR) << "Received unexpect stanza of type \"" << type << "\"";
    120     return false;
    121   }
    122 
    123   const XmlElement* result_element = response->FirstNamed(QName(
    124       kChromotingXmlNamespace, kRegisterQueryResultTag));
    125   if (!result_element) {
    126     LOG(ERROR) << "<" << kRegisterQueryResultTag
    127                << "> is missing in the host registration response: "
    128                << response->Str();
    129     return false;
    130   }
    131 
    132   const XmlElement* support_id_element =
    133       result_element->FirstNamed(QName(kChromotingXmlNamespace, kSupportIdTag));
    134   if (!support_id_element) {
    135     LOG(ERROR) << "<" << kSupportIdTag
    136                << "> is missing in the host registration response: "
    137                << response->Str();
    138     return false;
    139   }
    140 
    141   const XmlElement* lifetime_element =
    142       result_element->FirstNamed(QName(kChromotingXmlNamespace,
    143                                        kSupportIdLifetimeTag));
    144   if (!lifetime_element) {
    145     LOG(ERROR) << "<" << kSupportIdLifetimeTag
    146                << "> is missing in the host registration response: "
    147                << response->Str();
    148     return false;
    149   }
    150 
    151   int lifetime_int;
    152   if (!base::StringToInt(lifetime_element->BodyText().c_str(), &lifetime_int) ||
    153       lifetime_int <= 0) {
    154     LOG(ERROR) << "<" << kSupportIdLifetimeTag
    155                << "> is malformed in the host registration response: "
    156                << response->Str();
    157     return false;
    158   }
    159 
    160   *support_id = support_id_element->BodyText();
    161   *lifetime = base::TimeDelta::FromSeconds(lifetime_int);
    162   return true;
    163 }
    164 
    165 void RegisterSupportHostRequest::ProcessResponse(IqRequest* request,
    166                                                  const XmlElement* response) {
    167   std::string support_id;
    168   base::TimeDelta lifetime;
    169   bool success = ParseResponse(response, &support_id, &lifetime);
    170   CallCallback(success, support_id, lifetime);
    171 }
    172 
    173 void RegisterSupportHostRequest::CallCallback(
    174     bool success, const std::string& support_id, base::TimeDelta lifetime) {
    175   // Cleanup state before calling the callback.
    176   request_.reset();
    177   iq_sender_.reset();
    178   signal_strategy_->RemoveListener(this);
    179   signal_strategy_ = NULL;
    180 
    181   RegisterCallback callback = callback_;
    182   callback_.Reset();
    183   callback.Run(success, support_id, lifetime);
    184 }
    185 
    186 }  // namespace remoting
    187