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/signal_strategy.h"
17 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
18 #include "third_party/webrtc/libjingle/xmpp/constants.h"
23 scoped_ptr
<buzz::XmlElement
> IqSender::MakeIqStanza(
24 const std::string
& type
,
25 const std::string
& addressee
,
26 scoped_ptr
<buzz::XmlElement
> iq_body
) {
27 scoped_ptr
<buzz::XmlElement
> stanza(new buzz::XmlElement(buzz::QN_IQ
));
28 stanza
->AddAttr(buzz::QN_TYPE
, type
);
29 if (!addressee
.empty())
30 stanza
->AddAttr(buzz::QN_TO
, addressee
);
31 stanza
->AddElement(iq_body
.release());
35 IqSender::IqSender(SignalStrategy
* signal_strategy
)
36 : signal_strategy_(signal_strategy
) {
37 signal_strategy_
->AddListener(this);
40 IqSender::~IqSender() {
41 signal_strategy_
->RemoveListener(this);
44 scoped_ptr
<IqRequest
> IqSender::SendIq(scoped_ptr
<buzz::XmlElement
> stanza
,
45 const ReplyCallback
& callback
) {
46 std::string addressee
= stanza
->Attr(buzz::QN_TO
);
47 std::string id
= signal_strategy_
->GetNextId();
48 stanza
->AddAttr(buzz::QN_ID
, id
);
49 if (!signal_strategy_
->SendStanza(stanza
.Pass())) {
52 DCHECK(requests_
.find(id
) == requests_
.end());
53 scoped_ptr
<IqRequest
> request(new IqRequest(this, callback
, addressee
));
54 if (!callback
.is_null())
55 requests_
[id
] = request
.get();
56 return request
.Pass();
59 scoped_ptr
<IqRequest
> IqSender::SendIq(const std::string
& type
,
60 const std::string
& addressee
,
61 scoped_ptr
<buzz::XmlElement
> iq_body
,
62 const ReplyCallback
& callback
) {
63 return SendIq(MakeIqStanza(type
, addressee
, iq_body
.Pass()), callback
);
66 void IqSender::RemoveRequest(IqRequest
* request
) {
67 IqRequestMap::iterator it
= requests_
.begin();
68 while (it
!= requests_
.end()) {
69 IqRequestMap::iterator cur
= it
;
71 if (cur
->second
== request
) {
78 void IqSender::OnSignalStrategyStateChange(SignalStrategy::State state
) {
81 bool IqSender::OnSignalStrategyIncomingStanza(const buzz::XmlElement
* stanza
) {
82 if (stanza
->Name() != buzz::QN_IQ
) {
83 LOG(WARNING
) << "Received unexpected non-IQ packet " << stanza
->Str();
87 const std::string
& type
= stanza
->Attr(buzz::QN_TYPE
);
89 LOG(WARNING
) << "IQ packet missing type " << stanza
->Str();
93 if (type
!= "result" && type
!= "error") {
97 const std::string
& id
= stanza
->Attr(buzz::QN_ID
);
99 LOG(WARNING
) << "IQ packet missing id " << stanza
->Str();
103 std::string from
= stanza
->Attr(buzz::QN_FROM
);
105 IqRequestMap::iterator it
= requests_
.find(id
);
106 if (it
== requests_
.end()) {
110 IqRequest
* request
= it
->second
;
112 if (request
->addressee_
!= from
) {
113 LOG(ERROR
) << "Received IQ response from from a invalid JID. Ignoring it."
114 << " Message received from: " << from
115 << " Original JID: " << request
->addressee_
;
120 request
->OnResponse(stanza
);
125 IqRequest::IqRequest(IqSender
* sender
, const IqSender::ReplyCallback
& callback
,
126 const std::string
& addressee
)
129 addressee_(addressee
) {
132 IqRequest::~IqRequest() {
133 sender_
->RemoveRequest(this);
136 void IqRequest::SetTimeout(base::TimeDelta timeout
) {
137 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
138 FROM_HERE
, base::Bind(&IqRequest::OnTimeout
, AsWeakPtr()), timeout
);
141 void IqRequest::CallCallback(const buzz::XmlElement
* stanza
) {
142 if (!callback_
.is_null())
143 base::ResetAndReturn(&callback_
).Run(this, stanza
);
146 void IqRequest::OnTimeout() {
147 CallCallback(nullptr);
150 void IqRequest::OnResponse(const buzz::XmlElement
* stanza
) {
151 // It's unsafe to delete signal strategy here, and the callback may
152 // want to do that, so we post task to invoke the callback later.
153 scoped_ptr
<buzz::XmlElement
> stanza_copy(new buzz::XmlElement(*stanza
));
154 base::ThreadTaskRunnerHandle::Get()->PostTask(
155 FROM_HERE
, base::Bind(&IqRequest::DeliverResponse
, AsWeakPtr(),
156 base::Passed(&stanza_copy
)));
159 void IqRequest::DeliverResponse(scoped_ptr
<buzz::XmlElement
> stanza
) {
160 CallCallback(stanza
.get());
163 } // namespace remoting