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.
5 #include "remoting/host/register_support_host_request.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"
20 using buzz::XmlElement
;
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";
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";
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
),
44 directory_bot_jid_(directory_bot_jid
),
46 DCHECK(signal_strategy_
);
47 DCHECK(key_pair_
.get());
48 signal_strategy_
->AddListener(this);
49 iq_sender_
.reset(new IqSender(signal_strategy_
));
52 RegisterSupportHostRequest::~RegisterSupportHostRequest() {
54 signal_strategy_
->RemoveListener(this);
57 void RegisterSupportHostRequest::OnSignalStrategyStateChange(
58 SignalStrategy::State state
) {
59 if (state
== SignalStrategy::CONNECTED
) {
60 DCHECK(!callback_
.is_null());
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());
73 bool RegisterSupportHostRequest::OnSignalStrategyIncomingStanza(
74 const buzz::XmlElement
* stanza
) {
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());
90 scoped_ptr
<XmlElement
> RegisterSupportHostRequest::CreateSignature(
91 const std::string
& jid
) {
92 scoped_ptr
<XmlElement
> signature_tag(new XmlElement(
93 QName(kChromotingXmlNamespace
, kSignatureTag
)));
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
);
100 std::string message
= jid
+ ' ' + time_str
;
101 std::string
signature(key_pair_
->SignMessage(message
));
102 signature_tag
->AddText(signature
);
104 return signature_tag
.Pass();
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: "
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
<< "\"";
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: "
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: "
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: "
152 if (!base::StringToInt(lifetime_element
->BodyText().c_str(), &lifetime_int
) ||
154 LOG(ERROR
) << "<" << kSupportIdLifetimeTag
155 << "> is malformed in the host registration response: "
160 *support_id
= support_id_element
->BodyText();
161 *lifetime
= base::TimeDelta::FromSeconds(lifetime_int
);
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
);
173 void RegisterSupportHostRequest::CallCallback(
174 bool success
, const std::string
& support_id
, base::TimeDelta lifetime
) {
175 // Cleanup state before calling the callback.
178 signal_strategy_
->RemoveListener(this);
179 signal_strategy_
= NULL
;
181 RegisterCallback callback
= callback_
;
183 callback
.Run(success
, support_id
, lifetime
);
186 } // namespace remoting