1 // Copyright 2014 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/signaling/iq_sender.h"
8 #include "base/callback_helpers.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/time/time.h"
16 #include "remoting/signaling/jid_util.h"
17 #include "remoting/signaling/signal_strategy.h"
18 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
19 #include "third_party/webrtc/libjingle/xmpp/constants.h"
24 scoped_ptr
<buzz::XmlElement
> IqSender::MakeIqStanza(
25 const std::string
& type
,
26 const std::string
& addressee
,
27 scoped_ptr
<buzz::XmlElement
> iq_body
) {
28 scoped_ptr
<buzz::XmlElement
> stanza(new buzz::XmlElement(buzz::QN_IQ
));
29 stanza
->AddAttr(buzz::QN_TYPE
, type
);
30 if (!addressee
.empty())
31 stanza
->AddAttr(buzz::QN_TO
, addressee
);
32 stanza
->AddElement(iq_body
.release());
36 IqSender::IqSender(SignalStrategy
* signal_strategy
)
37 : signal_strategy_(signal_strategy
) {
38 signal_strategy_
->AddListener(this);
41 IqSender::~IqSender() {
42 signal_strategy_
->RemoveListener(this);
45 scoped_ptr
<IqRequest
> IqSender::SendIq(scoped_ptr
<buzz::XmlElement
> stanza
,
46 const ReplyCallback
& callback
) {
47 std::string addressee
= stanza
->Attr(buzz::QN_TO
);
48 std::string id
= signal_strategy_
->GetNextId();
49 stanza
->AddAttr(buzz::QN_ID
, id
);
50 if (!signal_strategy_
->SendStanza(stanza
.Pass())) {
53 DCHECK(requests_
.find(id
) == requests_
.end());
54 scoped_ptr
<IqRequest
> request(new IqRequest(this, callback
, addressee
));
55 if (!callback
.is_null())
56 requests_
[id
] = request
.get();
57 return request
.Pass();
60 scoped_ptr
<IqRequest
> IqSender::SendIq(const std::string
& type
,
61 const std::string
& addressee
,
62 scoped_ptr
<buzz::XmlElement
> iq_body
,
63 const ReplyCallback
& callback
) {
64 return SendIq(MakeIqStanza(type
, addressee
, iq_body
.Pass()), callback
);
67 void IqSender::RemoveRequest(IqRequest
* request
) {
68 IqRequestMap::iterator it
= requests_
.begin();
69 while (it
!= requests_
.end()) {
70 IqRequestMap::iterator cur
= it
;
72 if (cur
->second
== request
) {
79 void IqSender::OnSignalStrategyStateChange(SignalStrategy::State state
) {
82 bool IqSender::OnSignalStrategyIncomingStanza(const buzz::XmlElement
* stanza
) {
83 if (stanza
->Name() != buzz::QN_IQ
) {
84 LOG(WARNING
) << "Received unexpected non-IQ packet " << stanza
->Str();
88 const std::string
& type
= stanza
->Attr(buzz::QN_TYPE
);
90 LOG(WARNING
) << "IQ packet missing type " << stanza
->Str();
94 if (type
!= "result" && type
!= "error") {
98 const std::string
& id
= stanza
->Attr(buzz::QN_ID
);
100 LOG(WARNING
) << "IQ packet missing id " << stanza
->Str();
104 std::string from
= stanza
->Attr(buzz::QN_FROM
);
106 IqRequestMap::iterator it
= requests_
.find(id
);
107 if (it
== requests_
.end()) {
111 IqRequest
* request
= it
->second
;
113 if (NormalizeJid(request
->addressee_
) != NormalizeJid(from
)) {
114 LOG(ERROR
) << "Received IQ response from an invalid JID. Ignoring it."
115 << " Message received from: " << from
116 << " Original JID: " << request
->addressee_
;
121 request
->OnResponse(stanza
);
126 IqRequest::IqRequest(IqSender
* sender
, const IqSender::ReplyCallback
& callback
,
127 const std::string
& addressee
)
130 addressee_(addressee
) {
133 IqRequest::~IqRequest() {
134 sender_
->RemoveRequest(this);
137 void IqRequest::SetTimeout(base::TimeDelta timeout
) {
138 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
139 FROM_HERE
, base::Bind(&IqRequest::OnTimeout
, AsWeakPtr()), timeout
);
142 void IqRequest::CallCallback(const buzz::XmlElement
* stanza
) {
143 if (!callback_
.is_null())
144 base::ResetAndReturn(&callback_
).Run(this, stanza
);
147 void IqRequest::OnTimeout() {
148 CallCallback(nullptr);
151 void IqRequest::OnResponse(const buzz::XmlElement
* stanza
) {
152 // It's unsafe to delete signal strategy here, and the callback may
153 // want to do that, so we post task to invoke the callback later.
154 scoped_ptr
<buzz::XmlElement
> stanza_copy(new buzz::XmlElement(*stanza
));
155 base::ThreadTaskRunnerHandle::Get()->PostTask(
156 FROM_HERE
, base::Bind(&IqRequest::DeliverResponse
, AsWeakPtr(),
157 base::Passed(&stanza_copy
)));
160 void IqRequest::DeliverResponse(scoped_ptr
<buzz::XmlElement
> stanza
) {
161 CallCallback(stanza
.get());
164 } // namespace remoting