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