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/jingle_glue/iq_sender.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/time/time.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"
22 scoped_ptr
<buzz::XmlElement
> IqSender::MakeIqStanza(
23 const std::string
& type
,
24 const std::string
& addressee
,
25 scoped_ptr
<buzz::XmlElement
> iq_body
) {
26 scoped_ptr
<buzz::XmlElement
> stanza(new buzz::XmlElement(buzz::QN_IQ
));
27 stanza
->AddAttr(buzz::QN_TYPE
, type
);
28 if (!addressee
.empty())
29 stanza
->AddAttr(buzz::QN_TO
, addressee
);
30 stanza
->AddElement(iq_body
.release());
34 IqSender::IqSender(SignalStrategy
* signal_strategy
)
35 : signal_strategy_(signal_strategy
) {
36 signal_strategy_
->AddListener(this);
39 IqSender::~IqSender() {
40 signal_strategy_
->RemoveListener(this);
43 scoped_ptr
<IqRequest
> IqSender::SendIq(scoped_ptr
<buzz::XmlElement
> stanza
,
44 const ReplyCallback
& callback
) {
45 std::string addressee
= stanza
->Attr(buzz::QN_TO
);
46 std::string id
= signal_strategy_
->GetNextId();
47 stanza
->AddAttr(buzz::QN_ID
, id
);
48 if (!signal_strategy_
->SendStanza(stanza
.Pass())) {
49 return scoped_ptr
<IqRequest
>();
51 DCHECK(requests_
.find(id
) == requests_
.end());
52 scoped_ptr
<IqRequest
> request(new IqRequest(this, callback
, addressee
));
53 if (!callback
.is_null())
54 requests_
[id
] = request
.get();
55 return request
.Pass();
58 scoped_ptr
<IqRequest
> IqSender::SendIq(const std::string
& type
,
59 const std::string
& addressee
,
60 scoped_ptr
<buzz::XmlElement
> iq_body
,
61 const ReplyCallback
& callback
) {
62 return SendIq(MakeIqStanza(type
, addressee
, iq_body
.Pass()), callback
);
65 void IqSender::RemoveRequest(IqRequest
* request
) {
66 IqRequestMap::iterator it
= requests_
.begin();
67 while (it
!= requests_
.end()) {
68 IqRequestMap::iterator cur
= it
;
70 if (cur
->second
== request
) {
77 void IqSender::OnSignalStrategyStateChange(SignalStrategy::State state
) {
80 bool IqSender::OnSignalStrategyIncomingStanza(const buzz::XmlElement
* stanza
) {
81 if (stanza
->Name() != buzz::QN_IQ
) {
82 LOG(WARNING
) << "Received unexpected non-IQ packet " << stanza
->Str();
86 const std::string
& type
= stanza
->Attr(buzz::QN_TYPE
);
88 LOG(WARNING
) << "IQ packet missing type " << stanza
->Str();
92 if (type
!= "result" && type
!= "error") {
96 const std::string
& id
= stanza
->Attr(buzz::QN_ID
);
98 LOG(WARNING
) << "IQ packet missing id " << stanza
->Str();
102 std::string from
= stanza
->Attr(buzz::QN_FROM
);
104 IqRequestMap::iterator it
= requests_
.find(id
);
106 // This is a hack to workaround the issue with the WCS changing IDs
107 // of IQ responses sent from client to host. Whenever we receive a
108 // stanza with an unknown ID we try to match it with an outstanding
109 // request sent to the same peer.
110 if (it
== requests_
.end()) {
111 for (it
= requests_
.begin(); it
!= requests_
.end(); ++it
) {
112 if (it
->second
->addressee_
== from
) {
118 if (it
== requests_
.end()) {
122 IqRequest
* request
= it
->second
;
124 if (request
->addressee_
!= from
) {
125 LOG(ERROR
) << "Received IQ response from from a invalid JID. Ignoring it."
126 << " Message received from: " << from
127 << " Original JID: " << request
->addressee_
;
132 request
->OnResponse(stanza
);
137 IqRequest::IqRequest(IqSender
* sender
, const IqSender::ReplyCallback
& callback
,
138 const std::string
& addressee
)
141 addressee_(addressee
) {
144 IqRequest::~IqRequest() {
145 sender_
->RemoveRequest(this);
148 void IqRequest::SetTimeout(base::TimeDelta timeout
) {
149 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
150 FROM_HERE
, base::Bind(&IqRequest::OnTimeout
, AsWeakPtr()), timeout
);
153 void IqRequest::CallCallback(const buzz::XmlElement
* stanza
) {
154 if (!callback_
.is_null()) {
155 IqSender::ReplyCallback
callback(callback_
);
157 callback
.Run(this, stanza
);
161 void IqRequest::OnTimeout() {
165 void IqRequest::OnResponse(const buzz::XmlElement
* stanza
) {
166 // It's unsafe to delete signal strategy here, and the callback may
167 // want to do that, so we post task to invoke the callback later.
168 scoped_ptr
<buzz::XmlElement
> stanza_copy(new buzz::XmlElement(*stanza
));
169 base::ThreadTaskRunnerHandle::Get()->PostTask(
170 FROM_HERE
, base::Bind(&IqRequest::DeliverResponse
, AsWeakPtr(),
171 base::Passed(&stanza_copy
)));
174 void IqRequest::DeliverResponse(scoped_ptr
<buzz::XmlElement
> stanza
) {
175 CallCallback(stanza
.get());
178 } // namespace remoting