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/protocol/jingle_messages.h"
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "remoting/base/constants.h"
10 #include "remoting/protocol/content_description.h"
11 #include "remoting/protocol/name_value_map.h"
12 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
15 using buzz::XmlElement
;
20 const char kJabberNamespace
[] = "jabber:client";
21 const char kJingleNamespace
[] = "urn:xmpp:jingle:1";
22 const char kP2PTransportNamespace
[] = "http://www.google.com/transport/p2p";
26 const char kEmptyNamespace
[] = "";
27 const char kXmlNamespace
[] = "http://www.w3.org/XML/1998/namespace";
29 const int kPortMin
= 1000;
30 const int kPortMax
= 65535;
32 const NameMapElement
<JingleMessage::ActionType
> kActionTypes
[] = {
33 { JingleMessage::SESSION_INITIATE
, "session-initiate" },
34 { JingleMessage::SESSION_ACCEPT
, "session-accept" },
35 { JingleMessage::SESSION_TERMINATE
, "session-terminate" },
36 { JingleMessage::SESSION_INFO
, "session-info" },
37 { JingleMessage::TRANSPORT_INFO
, "transport-info" },
40 const NameMapElement
<JingleMessage::Reason
> kReasons
[] = {
41 { JingleMessage::SUCCESS
, "success" },
42 { JingleMessage::DECLINE
, "decline" },
43 { JingleMessage::CANCEL
, "cancel" },
44 { JingleMessage::GENERAL_ERROR
, "general-error" },
45 { JingleMessage::INCOMPATIBLE_PARAMETERS
, "incompatible-parameters" },
48 bool ParseCandidate(const buzz::XmlElement
* element
,
49 JingleMessage::NamedCandidate
* candidate
) {
50 DCHECK(element
->Name() == QName(kP2PTransportNamespace
, "candidate"));
52 const std::string
& name
= element
->Attr(QName(kEmptyNamespace
, "name"));
53 const std::string
& address
= element
->Attr(QName(kEmptyNamespace
, "address"));
54 const std::string
& port_str
= element
->Attr(QName(kEmptyNamespace
, "port"));
55 const std::string
& type
= element
->Attr(QName(kEmptyNamespace
, "type"));
56 const std::string
& protocol
=
57 element
->Attr(QName(kEmptyNamespace
, "protocol"));
58 const std::string
& username
=
59 element
->Attr(QName(kEmptyNamespace
, "username"));
60 const std::string
& password
=
61 element
->Attr(QName(kEmptyNamespace
, "password"));
62 const std::string
& preference_str
=
63 element
->Attr(QName(kEmptyNamespace
, "preference"));
64 const std::string
& generation_str
=
65 element
->Attr(QName(kEmptyNamespace
, "generation"));
70 if (name
.empty() || address
.empty() || !base::StringToInt(port_str
, &port
) ||
71 port
< kPortMin
|| port
> kPortMax
|| type
.empty() || protocol
.empty() ||
72 username
.empty() || password
.empty() ||
73 !base::StringToDouble(preference_str
, &preference
) ||
74 !base::StringToInt(generation_str
, &generation
)) {
78 candidate
->name
= name
;
80 candidate
->candidate
.set_address(rtc::SocketAddress(address
, port
));
81 candidate
->candidate
.set_type(type
);
82 candidate
->candidate
.set_protocol(protocol
);
83 candidate
->candidate
.set_username(username
);
84 candidate
->candidate
.set_password(password
);
85 candidate
->candidate
.set_preference(static_cast<float>(preference
));
86 candidate
->candidate
.set_generation(generation
);
91 XmlElement
* FormatCandidate(const JingleMessage::NamedCandidate
& candidate
) {
93 new XmlElement(QName(kP2PTransportNamespace
, "candidate"));
94 result
->SetAttr(QName(kEmptyNamespace
, "name"), candidate
.name
);
95 result
->SetAttr(QName(kEmptyNamespace
, "address"),
96 candidate
.candidate
.address().ipaddr().ToString());
97 result
->SetAttr(QName(kEmptyNamespace
, "port"),
98 base::IntToString(candidate
.candidate
.address().port()));
99 result
->SetAttr(QName(kEmptyNamespace
, "type"), candidate
.candidate
.type());
100 result
->SetAttr(QName(kEmptyNamespace
, "protocol"),
101 candidate
.candidate
.protocol());
102 result
->SetAttr(QName(kEmptyNamespace
, "username"),
103 candidate
.candidate
.username());
104 result
->SetAttr(QName(kEmptyNamespace
, "password"),
105 candidate
.candidate
.password());
106 result
->SetAttr(QName(kEmptyNamespace
, "preference"),
107 base::DoubleToString(candidate
.candidate
.preference()));
108 result
->SetAttr(QName(kEmptyNamespace
, "generation"),
109 base::IntToString(candidate
.candidate
.generation()));
115 JingleMessage::NamedCandidate::NamedCandidate() {
118 JingleMessage::NamedCandidate::NamedCandidate(
119 const std::string
& name
,
120 const cricket::Candidate
& candidate
)
122 candidate(candidate
) {
126 bool JingleMessage::IsJingleMessage(const buzz::XmlElement
* stanza
) {
127 return stanza
->Name() == QName(kJabberNamespace
, "iq") &&
128 stanza
->Attr(QName(std::string(), "type")) == "set" &&
129 stanza
->FirstNamed(QName(kJingleNamespace
, "jingle")) != nullptr;
133 std::string
JingleMessage::GetActionName(ActionType action
) {
134 return ValueToName(kActionTypes
, action
);
137 JingleMessage::JingleMessage()
138 : action(UNKNOWN_ACTION
),
139 reason(UNKNOWN_REASON
) {
142 JingleMessage::JingleMessage(
143 const std::string
& to_value
,
144 ActionType action_value
,
145 const std::string
& sid_value
)
147 action(action_value
),
149 reason(UNKNOWN_REASON
) {
152 JingleMessage::~JingleMessage() {
155 bool JingleMessage::ParseXml(const buzz::XmlElement
* stanza
,
156 std::string
* error
) {
157 if (!IsJingleMessage(stanza
)) {
158 *error
= "Not a jingle message";
162 const XmlElement
* jingle_tag
=
163 stanza
->FirstNamed(QName(kJingleNamespace
, "jingle"));
165 *error
= "Not a jingle message";
169 from
= stanza
->Attr(QName(kEmptyNamespace
, "from"));
170 to
= stanza
->Attr(QName(kEmptyNamespace
, "to"));
171 initiator
= jingle_tag
->Attr(QName(kEmptyNamespace
, "initiator"));
173 std::string action_str
= jingle_tag
->Attr(QName(kEmptyNamespace
, "action"));
174 if (action_str
.empty()) {
175 *error
= "action attribute is missing";
178 if (!NameToValue(kActionTypes
, action_str
, &action
)) {
179 *error
= "Unknown action " + action_str
;
183 sid
= jingle_tag
->Attr(QName(kEmptyNamespace
, "sid"));
185 *error
= "sid attribute is missing";
189 if (action
== SESSION_INFO
) {
190 // session-info messages may contain arbitrary information not
191 // defined by the Jingle protocol. We don't need to parse it.
192 const XmlElement
* child
= jingle_tag
->FirstElement();
194 // session-info is allowed to be empty.
195 info
.reset(new XmlElement(*child
));
202 const XmlElement
* reason_tag
=
203 jingle_tag
->FirstNamed(QName(kJingleNamespace
, "reason"));
204 if (reason_tag
&& reason_tag
->FirstElement()) {
205 if (!NameToValue(kReasons
, reason_tag
->FirstElement()->Name().LocalPart(),
207 reason
= UNKNOWN_REASON
;
211 if (action
== SESSION_TERMINATE
)
214 const XmlElement
* content_tag
=
215 jingle_tag
->FirstNamed(QName(kJingleNamespace
, "content"));
217 *error
= "content tag is missing";
221 std::string content_name
= content_tag
->Attr(QName(kEmptyNamespace
, "name"));
222 if (content_name
!= ContentDescription::kChromotingContentName
) {
223 *error
= "Unexpected content name: " + content_name
;
227 description
.reset(nullptr);
228 if (action
== SESSION_INITIATE
|| action
== SESSION_ACCEPT
) {
229 const XmlElement
* description_tag
= content_tag
->FirstNamed(
230 QName(kChromotingXmlNamespace
, "description"));
231 if (!description_tag
) {
232 *error
= "Missing chromoting content description";
236 description
= ContentDescription::ParseXml(description_tag
);
237 if (!description
.get()) {
238 *error
= "Failed to parse content description";
244 const XmlElement
* transport_tag
= content_tag
->FirstNamed(
245 QName(kP2PTransportNamespace
, "transport"));
247 QName
qn_candidate(kP2PTransportNamespace
, "candidate");
248 for (const XmlElement
* candidate_tag
=
249 transport_tag
->FirstNamed(qn_candidate
);
250 candidate_tag
!= nullptr;
251 candidate_tag
= candidate_tag
->NextNamed(qn_candidate
)) {
252 NamedCandidate candidate
;
253 if (!ParseCandidate(candidate_tag
, &candidate
)) {
254 *error
= "Failed to parse candidates";
257 candidates
.push_back(candidate
);
264 scoped_ptr
<buzz::XmlElement
> JingleMessage::ToXml() const {
265 scoped_ptr
<XmlElement
> root(
266 new XmlElement(QName("jabber:client", "iq"), true));
269 root
->AddAttr(QName(kEmptyNamespace
, "to"), to
);
271 root
->AddAttr(QName(kEmptyNamespace
, "from"), from
);
272 root
->SetAttr(QName(kEmptyNamespace
, "type"), "set");
274 XmlElement
* jingle_tag
=
275 new XmlElement(QName(kJingleNamespace
, "jingle"), true);
276 root
->AddElement(jingle_tag
);
277 jingle_tag
->AddAttr(QName(kEmptyNamespace
, "sid"), sid
);
279 const char* action_attr
= ValueToName(kActionTypes
, action
);
281 LOG(FATAL
) << "Invalid action value " << action
;
282 jingle_tag
->AddAttr(QName(kEmptyNamespace
, "action"), action_attr
);
284 if (action
== SESSION_INFO
) {
286 jingle_tag
->AddElement(new XmlElement(*info
.get()));
290 if (action
== SESSION_INITIATE
)
291 jingle_tag
->AddAttr(QName(kEmptyNamespace
, "initiator"), initiator
);
293 if (reason
!= UNKNOWN_REASON
) {
294 XmlElement
* reason_tag
= new XmlElement(QName(kJingleNamespace
, "reason"));
295 jingle_tag
->AddElement(reason_tag
);
296 const char* reason_string
=
297 ValueToName(kReasons
, reason
);
299 LOG(FATAL
) << "Invalid reason: " << reason
;
300 reason_tag
->AddElement(new XmlElement(
301 QName(kJingleNamespace
, reason_string
)));
304 if (action
!= SESSION_TERMINATE
) {
305 XmlElement
* content_tag
=
306 new XmlElement(QName(kJingleNamespace
, "content"));
307 jingle_tag
->AddElement(content_tag
);
309 content_tag
->AddAttr(QName(kEmptyNamespace
, "name"),
310 ContentDescription::kChromotingContentName
);
311 content_tag
->AddAttr(QName(kEmptyNamespace
, "creator"), "initiator");
313 if (description
.get())
314 content_tag
->AddElement(description
->ToXml());
316 XmlElement
* transport_tag
=
317 new XmlElement(QName(kP2PTransportNamespace
, "transport"), true);
318 content_tag
->AddElement(transport_tag
);
319 for (std::list
<NamedCandidate
>::const_iterator it
= candidates
.begin();
320 it
!= candidates
.end(); ++it
) {
321 transport_tag
->AddElement(FormatCandidate(*it
));
328 JingleMessageReply::JingleMessageReply()
329 : type(REPLY_RESULT
),
333 JingleMessageReply::JingleMessageReply(ErrorType error
)
334 : type(error
!= NONE
? REPLY_ERROR
: REPLY_RESULT
),
338 JingleMessageReply::JingleMessageReply(ErrorType error
,
339 const std::string
& text_value
)
345 JingleMessageReply::~JingleMessageReply() { }
347 scoped_ptr
<buzz::XmlElement
> JingleMessageReply::ToXml(
348 const buzz::XmlElement
* request_stanza
) const {
349 scoped_ptr
<XmlElement
> iq(
350 new XmlElement(QName(kJabberNamespace
, "iq"), true));
351 iq
->SetAttr(QName(kEmptyNamespace
, "to"),
352 request_stanza
->Attr(QName(kEmptyNamespace
, "from")));
353 iq
->SetAttr(QName(kEmptyNamespace
, "id"),
354 request_stanza
->Attr(QName(kEmptyNamespace
, "id")));
356 if (type
== REPLY_RESULT
) {
357 iq
->SetAttr(QName(kEmptyNamespace
, "type"), "result");
361 DCHECK_EQ(type
, REPLY_ERROR
);
363 iq
->SetAttr(QName(kEmptyNamespace
, "type"), "error");
365 for (const buzz::XmlElement
* child
= request_stanza
->FirstElement();
366 child
!= nullptr; child
= child
->NextElement()) {
367 iq
->AddElement(new buzz::XmlElement(*child
));
370 buzz::XmlElement
* error
=
371 new buzz::XmlElement(QName(kJabberNamespace
, "error"));
372 iq
->AddElement(error
);
375 std::string error_text
;
377 switch (error_type
) {
380 name
= QName(kJabberNamespace
, "bad-request");
382 case NOT_IMPLEMENTED
:
384 name
= QName(kJabberNamespace
, "feature-bad-request");
388 name
= QName(kJabberNamespace
, "item-not-found");
389 error_text
= "Invalid SID";
391 case UNEXPECTED_REQUEST
:
393 name
= QName(kJabberNamespace
, "unexpected-request");
395 case UNSUPPORTED_INFO
:
397 name
= QName(kJabberNamespace
, "feature-not-implemented");
406 error
->SetAttr(QName(kEmptyNamespace
, "type"), type
);
408 // If the error name is not in the standard namespace, we have
409 // to first add some error from that namespace.
410 if (name
.Namespace() != kJabberNamespace
) {
412 new buzz::XmlElement(QName(kJabberNamespace
, "undefined-condition")));
414 error
->AddElement(new buzz::XmlElement(name
));
416 if (!error_text
.empty()) {
417 // It's okay to always use English here. This text is for
418 // debugging purposes only.
419 buzz::XmlElement
* text_elem
=
420 new buzz::XmlElement(QName(kJabberNamespace
, "text"));
421 text_elem
->SetAttr(QName(kXmlNamespace
, "lang"), "en");
422 text_elem
->SetBodyText(error_text
);
423 error
->AddElement(text_elem
);
429 } // namespace protocol
430 } // namespace remoting