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
;
22 const char kJabberNamespace
[] = "jabber:client";
23 const char kJingleNamespace
[] = "urn:xmpp:jingle:1";
25 // Namespace for transport messages when using standard ICE.
26 const char kIceTransportNamespace
[] = "google:remoting:ice";
28 const char kEmptyNamespace
[] = "";
29 const char kXmlNamespace
[] = "http://www.w3.org/XML/1998/namespace";
31 const int kPortMin
= 1000;
32 const int kPortMax
= 65535;
34 const NameMapElement
<JingleMessage::ActionType
> kActionTypes
[] = {
35 { JingleMessage::SESSION_INITIATE
, "session-initiate" },
36 { JingleMessage::SESSION_ACCEPT
, "session-accept" },
37 { JingleMessage::SESSION_TERMINATE
, "session-terminate" },
38 { JingleMessage::SESSION_INFO
, "session-info" },
39 { JingleMessage::TRANSPORT_INFO
, "transport-info" },
42 const NameMapElement
<JingleMessage::Reason
> kReasons
[] = {
43 { JingleMessage::SUCCESS
, "success" },
44 { JingleMessage::DECLINE
, "decline" },
45 { JingleMessage::CANCEL
, "cancel" },
46 { JingleMessage::GENERAL_ERROR
, "general-error" },
47 { JingleMessage::INCOMPATIBLE_PARAMETERS
, "incompatible-parameters" },
50 bool ParseIceCredentials(const buzz::XmlElement
* element
,
51 JingleMessage::IceCredentials
* credentials
) {
52 DCHECK(element
->Name() == QName(kIceTransportNamespace
, "credentials"));
54 const std::string
& channel
= element
->Attr(QName(kEmptyNamespace
, "channel"));
55 const std::string
& ufrag
=
56 element
->Attr(QName(kEmptyNamespace
, "ufrag"));
57 const std::string
& password
=
58 element
->Attr(QName(kEmptyNamespace
, "password"));
60 if (channel
.empty() || ufrag
.empty() || password
.empty()) {
64 credentials
->channel
= channel
;
65 credentials
->ufrag
= ufrag
;
66 credentials
->password
= password
;
71 bool ParseIceCandidate(const buzz::XmlElement
* element
,
72 JingleMessage::NamedCandidate
* candidate
) {
73 DCHECK(element
->Name() == QName(kIceTransportNamespace
, "candidate"));
75 const std::string
& name
= element
->Attr(QName(kEmptyNamespace
, "name"));
76 const std::string
& foundation
=
77 element
->Attr(QName(kEmptyNamespace
, "foundation"));
78 const std::string
& address
= element
->Attr(QName(kEmptyNamespace
, "address"));
79 const std::string
& port_str
= element
->Attr(QName(kEmptyNamespace
, "port"));
80 const std::string
& type
= element
->Attr(QName(kEmptyNamespace
, "type"));
81 const std::string
& protocol
=
82 element
->Attr(QName(kEmptyNamespace
, "protocol"));
83 const std::string
& priority_str
=
84 element
->Attr(QName(kEmptyNamespace
, "priority"));
85 const std::string
& generation_str
=
86 element
->Attr(QName(kEmptyNamespace
, "generation"));
91 if (name
.empty() || foundation
.empty() || address
.empty() ||
92 !base::StringToInt(port_str
, &port
) || port
< kPortMin
||
93 port
> kPortMax
|| type
.empty() || protocol
.empty() ||
94 !base::StringToUint(priority_str
, &priority
) ||
95 !base::StringToInt(generation_str
, &generation
)) {
99 candidate
->name
= name
;
101 candidate
->candidate
.set_foundation(foundation
);
102 candidate
->candidate
.set_address(rtc::SocketAddress(address
, port
));
103 candidate
->candidate
.set_type(type
);
104 candidate
->candidate
.set_protocol(protocol
);
105 candidate
->candidate
.set_priority(priority
);
106 candidate
->candidate
.set_generation(generation
);
111 bool ParseIceTransportInfo(
112 const buzz::XmlElement
* element
,
113 std::list
<JingleMessage::IceCredentials
>* ice_credentials
,
114 std::list
<JingleMessage::NamedCandidate
>* candidates
) {
115 DCHECK(element
->Name() == QName(kIceTransportNamespace
, "transport"));
117 ice_credentials
->clear();
120 QName
qn_credentials(kIceTransportNamespace
, "credentials");
121 for (const XmlElement
* credentials_tag
= element
->FirstNamed(qn_credentials
);
123 credentials_tag
= credentials_tag
->NextNamed(qn_credentials
)) {
124 JingleMessage::IceCredentials credentials
;
125 if (!ParseIceCredentials(credentials_tag
, &credentials
))
127 ice_credentials
->push_back(credentials
);
130 QName
qn_candidate(kIceTransportNamespace
, "candidate");
131 for (const XmlElement
* candidate_tag
= element
->FirstNamed(qn_candidate
);
132 candidate_tag
; candidate_tag
= candidate_tag
->NextNamed(qn_candidate
)) {
133 JingleMessage::NamedCandidate candidate
;
134 if (!ParseIceCandidate(candidate_tag
, &candidate
))
136 candidates
->push_back(candidate
);
142 XmlElement
* FormatIceCredentials(
143 const JingleMessage::IceCredentials
& credentials
) {
145 new XmlElement(QName(kIceTransportNamespace
, "credentials"));
146 result
->SetAttr(QName(kEmptyNamespace
, "channel"), credentials
.channel
);
147 result
->SetAttr(QName(kEmptyNamespace
, "ufrag"), credentials
.ufrag
);
148 result
->SetAttr(QName(kEmptyNamespace
, "password"), credentials
.password
);
152 XmlElement
* FormatIceCandidate(const JingleMessage::NamedCandidate
& candidate
) {
154 new XmlElement(QName(kIceTransportNamespace
, "candidate"));
155 result
->SetAttr(QName(kEmptyNamespace
, "name"), candidate
.name
);
156 result
->SetAttr(QName(kEmptyNamespace
, "foundation"),
157 candidate
.candidate
.foundation());
158 result
->SetAttr(QName(kEmptyNamespace
, "address"),
159 candidate
.candidate
.address().ipaddr().ToString());
160 result
->SetAttr(QName(kEmptyNamespace
, "port"),
161 base::IntToString(candidate
.candidate
.address().port()));
162 result
->SetAttr(QName(kEmptyNamespace
, "type"), candidate
.candidate
.type());
163 result
->SetAttr(QName(kEmptyNamespace
, "protocol"),
164 candidate
.candidate
.protocol());
165 result
->SetAttr(QName(kEmptyNamespace
, "priority"),
166 base::DoubleToString(candidate
.candidate
.priority()));
167 result
->SetAttr(QName(kEmptyNamespace
, "generation"),
168 base::IntToString(candidate
.candidate
.generation()));
174 JingleMessage::NamedCandidate::NamedCandidate(
175 const std::string
& name
,
176 const cricket::Candidate
& candidate
)
178 candidate(candidate
) {
181 JingleMessage::IceCredentials::IceCredentials(std::string channel
,
183 std::string password
)
184 : channel(channel
), ufrag(ufrag
), password(password
) {
188 bool JingleMessage::IsJingleMessage(const buzz::XmlElement
* stanza
) {
189 return stanza
->Name() == QName(kJabberNamespace
, "iq") &&
190 stanza
->Attr(QName(std::string(), "type")) == "set" &&
191 stanza
->FirstNamed(QName(kJingleNamespace
, "jingle")) != nullptr;
195 std::string
JingleMessage::GetActionName(ActionType action
) {
196 return ValueToName(kActionTypes
, action
);
199 JingleMessage::JingleMessage() {}
201 JingleMessage::JingleMessage(const std::string
& to
,
203 const std::string
& sid
)
204 : to(to
), action(action
), sid(sid
) {}
206 JingleMessage::~JingleMessage() {}
208 bool JingleMessage::ParseXml(const buzz::XmlElement
* stanza
,
209 std::string
* error
) {
210 if (!IsJingleMessage(stanza
)) {
211 *error
= "Not a jingle message";
215 const XmlElement
* jingle_tag
=
216 stanza
->FirstNamed(QName(kJingleNamespace
, "jingle"));
218 *error
= "Not a jingle message";
222 from
= stanza
->Attr(QName(kEmptyNamespace
, "from"));
223 to
= stanza
->Attr(QName(kEmptyNamespace
, "to"));
224 initiator
= jingle_tag
->Attr(QName(kEmptyNamespace
, "initiator"));
226 std::string action_str
= jingle_tag
->Attr(QName(kEmptyNamespace
, "action"));
227 if (action_str
.empty()) {
228 *error
= "action attribute is missing";
231 if (!NameToValue(kActionTypes
, action_str
, &action
)) {
232 *error
= "Unknown action " + action_str
;
236 sid
= jingle_tag
->Attr(QName(kEmptyNamespace
, "sid"));
238 *error
= "sid attribute is missing";
242 if (action
== SESSION_INFO
) {
243 // session-info messages may contain arbitrary information not
244 // defined by the Jingle protocol. We don't need to parse it.
245 const XmlElement
* child
= jingle_tag
->FirstElement();
247 // session-info is allowed to be empty.
248 info
.reset(new XmlElement(*child
));
255 const XmlElement
* reason_tag
=
256 jingle_tag
->FirstNamed(QName(kJingleNamespace
, "reason"));
257 if (reason_tag
&& reason_tag
->FirstElement()) {
258 if (!NameToValue(kReasons
, reason_tag
->FirstElement()->Name().LocalPart(),
260 reason
= UNKNOWN_REASON
;
264 if (action
== SESSION_TERMINATE
)
267 const XmlElement
* content_tag
=
268 jingle_tag
->FirstNamed(QName(kJingleNamespace
, "content"));
270 *error
= "content tag is missing";
274 std::string content_name
= content_tag
->Attr(QName(kEmptyNamespace
, "name"));
275 if (content_name
!= ContentDescription::kChromotingContentName
) {
276 *error
= "Unexpected content name: " + content_name
;
280 description
.reset(nullptr);
281 if (action
== SESSION_INITIATE
|| action
== SESSION_ACCEPT
) {
282 const XmlElement
* description_tag
= content_tag
->FirstNamed(
283 QName(kChromotingXmlNamespace
, "description"));
284 if (!description_tag
) {
285 *error
= "Missing chromoting content description";
289 description
= ContentDescription::ParseXml(description_tag
);
290 if (!description
.get()) {
291 *error
= "Failed to parse content description";
296 const XmlElement
* ice_transport_tag
= content_tag
->FirstNamed(
297 QName(kIceTransportNamespace
, "transport"));
298 if (ice_transport_tag
) {
299 if (!ParseIceTransportInfo(ice_transport_tag
, &ice_credentials
,
301 *error
= "Failed to parse transport info";
309 scoped_ptr
<buzz::XmlElement
> JingleMessage::ToXml() const {
310 scoped_ptr
<XmlElement
> root(
311 new XmlElement(QName("jabber:client", "iq"), true));
314 root
->AddAttr(QName(kEmptyNamespace
, "to"), to
);
316 root
->AddAttr(QName(kEmptyNamespace
, "from"), from
);
317 root
->SetAttr(QName(kEmptyNamespace
, "type"), "set");
319 XmlElement
* jingle_tag
=
320 new XmlElement(QName(kJingleNamespace
, "jingle"), true);
321 root
->AddElement(jingle_tag
);
322 jingle_tag
->AddAttr(QName(kEmptyNamespace
, "sid"), sid
);
324 const char* action_attr
= ValueToName(kActionTypes
, action
);
326 LOG(FATAL
) << "Invalid action value " << action
;
327 jingle_tag
->AddAttr(QName(kEmptyNamespace
, "action"), action_attr
);
329 if (action
== SESSION_INFO
) {
331 jingle_tag
->AddElement(new XmlElement(*info
.get()));
335 if (action
== SESSION_INITIATE
)
336 jingle_tag
->AddAttr(QName(kEmptyNamespace
, "initiator"), initiator
);
338 if (reason
!= UNKNOWN_REASON
) {
339 XmlElement
* reason_tag
= new XmlElement(QName(kJingleNamespace
, "reason"));
340 jingle_tag
->AddElement(reason_tag
);
341 const char* reason_string
=
342 ValueToName(kReasons
, reason
);
344 LOG(FATAL
) << "Invalid reason: " << reason
;
345 reason_tag
->AddElement(new XmlElement(
346 QName(kJingleNamespace
, reason_string
)));
349 if (action
!= SESSION_TERMINATE
) {
350 XmlElement
* content_tag
=
351 new XmlElement(QName(kJingleNamespace
, "content"));
352 jingle_tag
->AddElement(content_tag
);
354 content_tag
->AddAttr(QName(kEmptyNamespace
, "name"),
355 ContentDescription::kChromotingContentName
);
356 content_tag
->AddAttr(QName(kEmptyNamespace
, "creator"), "initiator");
358 if (description
.get())
359 content_tag
->AddElement(description
->ToXml());
361 if (!ice_credentials
.empty() || !candidates
.empty()) {
362 XmlElement
* transport_tag
=
363 new XmlElement(QName(kIceTransportNamespace
, "transport"), true);
364 content_tag
->AddElement(transport_tag
);
365 for (const IceCredentials
& credentials
: ice_credentials
) {
366 transport_tag
->AddElement(FormatIceCredentials(credentials
));
368 for (const NamedCandidate
& candidate
: candidates
) {
369 transport_tag
->AddElement(FormatIceCandidate(candidate
));
377 JingleMessageReply::JingleMessageReply()
378 : type(REPLY_RESULT
),
382 JingleMessageReply::JingleMessageReply(ErrorType error
)
383 : type(error
!= NONE
? REPLY_ERROR
: REPLY_RESULT
),
387 JingleMessageReply::JingleMessageReply(ErrorType error
,
388 const std::string
& text_value
)
394 JingleMessageReply::~JingleMessageReply() { }
396 scoped_ptr
<buzz::XmlElement
> JingleMessageReply::ToXml(
397 const buzz::XmlElement
* request_stanza
) const {
398 scoped_ptr
<XmlElement
> iq(
399 new XmlElement(QName(kJabberNamespace
, "iq"), true));
400 iq
->SetAttr(QName(kEmptyNamespace
, "to"),
401 request_stanza
->Attr(QName(kEmptyNamespace
, "from")));
402 iq
->SetAttr(QName(kEmptyNamespace
, "id"),
403 request_stanza
->Attr(QName(kEmptyNamespace
, "id")));
405 if (type
== REPLY_RESULT
) {
406 iq
->SetAttr(QName(kEmptyNamespace
, "type"), "result");
410 DCHECK_EQ(type
, REPLY_ERROR
);
412 iq
->SetAttr(QName(kEmptyNamespace
, "type"), "error");
414 for (const buzz::XmlElement
* child
= request_stanza
->FirstElement();
415 child
!= nullptr; child
= child
->NextElement()) {
416 iq
->AddElement(new buzz::XmlElement(*child
));
419 buzz::XmlElement
* error
=
420 new buzz::XmlElement(QName(kJabberNamespace
, "error"));
421 iq
->AddElement(error
);
424 std::string error_text
;
426 switch (error_type
) {
429 name
= QName(kJabberNamespace
, "bad-request");
431 case NOT_IMPLEMENTED
:
433 name
= QName(kJabberNamespace
, "feature-bad-request");
437 name
= QName(kJabberNamespace
, "item-not-found");
438 error_text
= "Invalid SID";
440 case UNEXPECTED_REQUEST
:
442 name
= QName(kJabberNamespace
, "unexpected-request");
444 case UNSUPPORTED_INFO
:
446 name
= QName(kJabberNamespace
, "feature-not-implemented");
455 error
->SetAttr(QName(kEmptyNamespace
, "type"), type
);
457 // If the error name is not in the standard namespace, we have
458 // to first add some error from that namespace.
459 if (name
.Namespace() != kJabberNamespace
) {
461 new buzz::XmlElement(QName(kJabberNamespace
, "undefined-condition")));
463 error
->AddElement(new buzz::XmlElement(name
));
465 if (!error_text
.empty()) {
466 // It's okay to always use English here. This text is for
467 // debugging purposes only.
468 buzz::XmlElement
* text_elem
=
469 new buzz::XmlElement(QName(kJabberNamespace
, "text"));
470 text_elem
->SetAttr(QName(kXmlNamespace
, "lang"), "en");
471 text_elem
->SetBodyText(error_text
);
472 error
->AddElement(text_elem
);
478 } // namespace protocol
479 } // namespace remoting