Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / protocol / jingle_messages.cc
blob7e4c21ee3bb537264b29c9070701b5217abad643
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"
14 using buzz::QName;
15 using buzz::XmlElement;
17 namespace remoting {
18 namespace protocol {
20 namespace {
22 const char kJabberNamespace[] = "jabber:client";
23 const char kJingleNamespace[] = "urn:xmpp:jingle:1";
25 // Namespace for transport messages for legacy GICE.
26 const char kGiceTransportNamespace[] = "http://www.google.com/transport/p2p";
28 // Namespace for transport messages when using standard ICE.
29 const char kIceTransportNamespace[] = "google:remoting:ice";
31 const char kEmptyNamespace[] = "";
32 const char kXmlNamespace[] = "http://www.w3.org/XML/1998/namespace";
34 const int kPortMin = 1000;
35 const int kPortMax = 65535;
37 const NameMapElement<JingleMessage::ActionType> kActionTypes[] = {
38 { JingleMessage::SESSION_INITIATE, "session-initiate" },
39 { JingleMessage::SESSION_ACCEPT, "session-accept" },
40 { JingleMessage::SESSION_TERMINATE, "session-terminate" },
41 { JingleMessage::SESSION_INFO, "session-info" },
42 { JingleMessage::TRANSPORT_INFO, "transport-info" },
45 const NameMapElement<JingleMessage::Reason> kReasons[] = {
46 { JingleMessage::SUCCESS, "success" },
47 { JingleMessage::DECLINE, "decline" },
48 { JingleMessage::CANCEL, "cancel" },
49 { JingleMessage::GENERAL_ERROR, "general-error" },
50 { JingleMessage::INCOMPATIBLE_PARAMETERS, "incompatible-parameters" },
53 bool ParseIceCredentials(const buzz::XmlElement* element,
54 JingleMessage::IceCredentials* credentials) {
55 DCHECK(element->Name() == QName(kIceTransportNamespace, "credentials"));
57 const std::string& channel = element->Attr(QName(kEmptyNamespace, "channel"));
58 const std::string& ufrag =
59 element->Attr(QName(kEmptyNamespace, "ufrag"));
60 const std::string& password =
61 element->Attr(QName(kEmptyNamespace, "password"));
63 if (channel.empty() || ufrag.empty() || password.empty()) {
64 return false;
67 credentials->channel = channel;
68 credentials->ufrag = ufrag;
69 credentials->password = password;
71 return true;
74 bool ParseIceCandidate(const buzz::XmlElement* element,
75 JingleMessage::NamedCandidate* candidate) {
76 DCHECK(element->Name() == QName(kIceTransportNamespace, "candidate"));
78 const std::string& name = element->Attr(QName(kEmptyNamespace, "name"));
79 const std::string& foundation =
80 element->Attr(QName(kEmptyNamespace, "foundation"));
81 const std::string& address = element->Attr(QName(kEmptyNamespace, "address"));
82 const std::string& port_str = element->Attr(QName(kEmptyNamespace, "port"));
83 const std::string& type = element->Attr(QName(kEmptyNamespace, "type"));
84 const std::string& protocol =
85 element->Attr(QName(kEmptyNamespace, "protocol"));
86 const std::string& priority_str =
87 element->Attr(QName(kEmptyNamespace, "priority"));
88 const std::string& generation_str =
89 element->Attr(QName(kEmptyNamespace, "generation"));
91 int port;
92 unsigned priority;
93 int generation;
94 if (name.empty() || foundation.empty() || address.empty() ||
95 !base::StringToInt(port_str, &port) || port < kPortMin ||
96 port > kPortMax || type.empty() || protocol.empty() ||
97 !base::StringToUint(priority_str, &priority) ||
98 !base::StringToInt(generation_str, &generation)) {
99 return false;
102 candidate->name = name;
104 candidate->candidate.set_foundation(foundation);
105 candidate->candidate.set_address(rtc::SocketAddress(address, port));
106 candidate->candidate.set_type(type);
107 candidate->candidate.set_protocol(protocol);
108 candidate->candidate.set_priority(priority);
109 candidate->candidate.set_generation(generation);
111 return true;
114 bool ParseIceTransportInfo(
115 const buzz::XmlElement* element,
116 std::list<JingleMessage::IceCredentials>* ice_credentials,
117 std::list<JingleMessage::NamedCandidate>* candidates) {
118 DCHECK(element->Name() == QName(kIceTransportNamespace, "transport"));
120 ice_credentials->clear();
121 candidates->clear();
123 QName qn_credentials(kIceTransportNamespace, "credentials");
124 for (const XmlElement* credentials_tag = element->FirstNamed(qn_credentials);
125 credentials_tag;
126 credentials_tag = credentials_tag->NextNamed(qn_credentials)) {
127 JingleMessage::IceCredentials credentials;
128 if (!ParseIceCredentials(credentials_tag, &credentials))
129 return false;
130 ice_credentials->push_back(credentials);
133 QName qn_candidate(kIceTransportNamespace, "candidate");
134 for (const XmlElement* candidate_tag = element->FirstNamed(qn_candidate);
135 candidate_tag; candidate_tag = candidate_tag->NextNamed(qn_candidate)) {
136 JingleMessage::NamedCandidate candidate;
137 if (!ParseIceCandidate(candidate_tag, &candidate))
138 return false;
139 candidates->push_back(candidate);
142 return true;
145 bool ParseGiceCandidate(const buzz::XmlElement* element,
146 JingleMessage::NamedCandidate* candidate) {
147 DCHECK(element->Name() == QName(kGiceTransportNamespace, "candidate"));
149 const std::string& name = element->Attr(QName(kEmptyNamespace, "name"));
150 const std::string& address = element->Attr(QName(kEmptyNamespace, "address"));
151 const std::string& port_str = element->Attr(QName(kEmptyNamespace, "port"));
152 const std::string& type = element->Attr(QName(kEmptyNamespace, "type"));
153 const std::string& protocol =
154 element->Attr(QName(kEmptyNamespace, "protocol"));
155 const std::string& username =
156 element->Attr(QName(kEmptyNamespace, "username"));
157 const std::string& password =
158 element->Attr(QName(kEmptyNamespace, "password"));
159 const std::string& preference_str =
160 element->Attr(QName(kEmptyNamespace, "preference"));
161 const std::string& generation_str =
162 element->Attr(QName(kEmptyNamespace, "generation"));
164 int port;
165 double preference;
166 int generation;
167 if (name.empty() || address.empty() || !base::StringToInt(port_str, &port) ||
168 port < kPortMin || port > kPortMax || type.empty() || protocol.empty() ||
169 username.empty() || password.empty() ||
170 !base::StringToDouble(preference_str, &preference) ||
171 !base::StringToInt(generation_str, &generation)) {
172 return false;
175 candidate->name = name;
177 candidate->candidate.set_address(rtc::SocketAddress(address, port));
178 candidate->candidate.set_type(type);
179 candidate->candidate.set_protocol(protocol);
180 candidate->candidate.set_username(username);
181 candidate->candidate.set_password(password);
182 candidate->candidate.set_preference(static_cast<float>(preference));
183 candidate->candidate.set_generation(generation);
185 return true;
188 bool ParseGiceTransportInfo(
189 const buzz::XmlElement* element,
190 std::list<JingleMessage::NamedCandidate>* candidates) {
191 DCHECK(element->Name() == QName(kGiceTransportNamespace, "transport"));
193 candidates->clear();
195 QName qn_candidate(kGiceTransportNamespace, "candidate");
196 for (const XmlElement* candidate_tag = element->FirstNamed(qn_candidate);
197 candidate_tag; candidate_tag = candidate_tag->NextNamed(qn_candidate)) {
198 JingleMessage::NamedCandidate candidate;
199 if (!ParseGiceCandidate(candidate_tag, &candidate))
200 return false;
201 candidates->push_back(candidate);
204 return true;
207 XmlElement* FormatIceCredentials(
208 const JingleMessage::IceCredentials& credentials) {
209 XmlElement* result =
210 new XmlElement(QName(kIceTransportNamespace, "credentials"));
211 result->SetAttr(QName(kEmptyNamespace, "channel"), credentials.channel);
212 result->SetAttr(QName(kEmptyNamespace, "ufrag"), credentials.ufrag);
213 result->SetAttr(QName(kEmptyNamespace, "password"), credentials.password);
214 return result;
217 XmlElement* FormatIceCandidate(const JingleMessage::NamedCandidate& candidate) {
218 XmlElement* result =
219 new XmlElement(QName(kIceTransportNamespace, "candidate"));
220 result->SetAttr(QName(kEmptyNamespace, "name"), candidate.name);
221 result->SetAttr(QName(kEmptyNamespace, "foundation"),
222 candidate.candidate.foundation());
223 result->SetAttr(QName(kEmptyNamespace, "address"),
224 candidate.candidate.address().ipaddr().ToString());
225 result->SetAttr(QName(kEmptyNamespace, "port"),
226 base::IntToString(candidate.candidate.address().port()));
227 result->SetAttr(QName(kEmptyNamespace, "type"), candidate.candidate.type());
228 result->SetAttr(QName(kEmptyNamespace, "protocol"),
229 candidate.candidate.protocol());
230 result->SetAttr(QName(kEmptyNamespace, "priority"),
231 base::DoubleToString(candidate.candidate.priority()));
232 result->SetAttr(QName(kEmptyNamespace, "generation"),
233 base::IntToString(candidate.candidate.generation()));
234 return result;
237 XmlElement* FormatGiceCandidate(
238 const JingleMessage::NamedCandidate& candidate) {
239 XmlElement* result =
240 new XmlElement(QName(kGiceTransportNamespace, "candidate"));
241 result->SetAttr(QName(kEmptyNamespace, "name"), candidate.name);
242 result->SetAttr(QName(kEmptyNamespace, "address"),
243 candidate.candidate.address().ipaddr().ToString());
244 result->SetAttr(QName(kEmptyNamespace, "port"),
245 base::IntToString(candidate.candidate.address().port()));
246 result->SetAttr(QName(kEmptyNamespace, "type"), candidate.candidate.type());
247 result->SetAttr(QName(kEmptyNamespace, "protocol"),
248 candidate.candidate.protocol());
249 result->SetAttr(QName(kEmptyNamespace, "username"),
250 candidate.candidate.username());
251 result->SetAttr(QName(kEmptyNamespace, "password"),
252 candidate.candidate.password());
253 result->SetAttr(QName(kEmptyNamespace, "preference"),
254 base::DoubleToString(candidate.candidate.preference()));
255 result->SetAttr(QName(kEmptyNamespace, "generation"),
256 base::IntToString(candidate.candidate.generation()));
257 return result;
260 } // namespace
262 JingleMessage::NamedCandidate::NamedCandidate(
263 const std::string& name,
264 const cricket::Candidate& candidate)
265 : name(name),
266 candidate(candidate) {
269 JingleMessage::IceCredentials::IceCredentials(std::string channel,
270 std::string ufrag,
271 std::string password)
272 : channel(channel), ufrag(ufrag), password(password) {
275 // static
276 bool JingleMessage::IsJingleMessage(const buzz::XmlElement* stanza) {
277 return stanza->Name() == QName(kJabberNamespace, "iq") &&
278 stanza->Attr(QName(std::string(), "type")) == "set" &&
279 stanza->FirstNamed(QName(kJingleNamespace, "jingle")) != nullptr;
282 // static
283 std::string JingleMessage::GetActionName(ActionType action) {
284 return ValueToName(kActionTypes, action);
287 JingleMessage::JingleMessage() {
290 JingleMessage::JingleMessage(const std::string& to,
291 ActionType action,
292 const std::string& sid)
293 : to(to), action(action), sid(sid) {
296 JingleMessage::~JingleMessage() {
299 bool JingleMessage::ParseXml(const buzz::XmlElement* stanza,
300 std::string* error) {
301 if (!IsJingleMessage(stanza)) {
302 *error = "Not a jingle message";
303 return false;
306 const XmlElement* jingle_tag =
307 stanza->FirstNamed(QName(kJingleNamespace, "jingle"));
308 if (!jingle_tag) {
309 *error = "Not a jingle message";
310 return false;
313 from = stanza->Attr(QName(kEmptyNamespace, "from"));
314 to = stanza->Attr(QName(kEmptyNamespace, "to"));
315 initiator = jingle_tag->Attr(QName(kEmptyNamespace, "initiator"));
317 std::string action_str = jingle_tag->Attr(QName(kEmptyNamespace, "action"));
318 if (action_str.empty()) {
319 *error = "action attribute is missing";
320 return false;
322 if (!NameToValue(kActionTypes, action_str, &action)) {
323 *error = "Unknown action " + action_str;
324 return false;
327 sid = jingle_tag->Attr(QName(kEmptyNamespace, "sid"));
328 if (sid.empty()) {
329 *error = "sid attribute is missing";
330 return false;
333 if (action == SESSION_INFO) {
334 // session-info messages may contain arbitrary information not
335 // defined by the Jingle protocol. We don't need to parse it.
336 const XmlElement* child = jingle_tag->FirstElement();
337 if (child) {
338 // session-info is allowed to be empty.
339 info.reset(new XmlElement(*child));
340 } else {
341 info.reset(nullptr);
343 return true;
346 const XmlElement* reason_tag =
347 jingle_tag->FirstNamed(QName(kJingleNamespace, "reason"));
348 if (reason_tag && reason_tag->FirstElement()) {
349 if (!NameToValue(kReasons, reason_tag->FirstElement()->Name().LocalPart(),
350 &reason)) {
351 reason = UNKNOWN_REASON;
355 if (action == SESSION_TERMINATE)
356 return true;
358 const XmlElement* content_tag =
359 jingle_tag->FirstNamed(QName(kJingleNamespace, "content"));
360 if (!content_tag) {
361 *error = "content tag is missing";
362 return false;
365 std::string content_name = content_tag->Attr(QName(kEmptyNamespace, "name"));
366 if (content_name != ContentDescription::kChromotingContentName) {
367 *error = "Unexpected content name: " + content_name;
368 return false;
371 description.reset(nullptr);
372 if (action == SESSION_INITIATE || action == SESSION_ACCEPT) {
373 const XmlElement* description_tag = content_tag->FirstNamed(
374 QName(kChromotingXmlNamespace, "description"));
375 if (!description_tag) {
376 *error = "Missing chromoting content description";
377 return false;
380 description = ContentDescription::ParseXml(description_tag);
381 if (!description.get()) {
382 *error = "Failed to parse content description";
383 return false;
387 const XmlElement* ice_transport_tag = content_tag->FirstNamed(
388 QName(kIceTransportNamespace, "transport"));
389 const XmlElement* gice_transport_tag = content_tag->FirstNamed(
390 QName(kGiceTransportNamespace, "transport"));
391 if (ice_transport_tag && gice_transport_tag) {
392 *error = "ICE and GICE transport information is found in the same message";
393 return false;
394 } else if (ice_transport_tag) {
395 standard_ice = true;
396 if (!ParseIceTransportInfo(ice_transport_tag, &ice_credentials,
397 &candidates)) {
398 *error = "Failed to parse transport info";
399 return false;
401 } else if (gice_transport_tag) {
402 standard_ice = false;
403 ice_credentials.clear();
404 if (!ParseGiceTransportInfo(gice_transport_tag, &candidates)) {
405 *error = "Failed to parse transport info";
406 return false;
410 return true;
413 scoped_ptr<buzz::XmlElement> JingleMessage::ToXml() const {
414 scoped_ptr<XmlElement> root(
415 new XmlElement(QName("jabber:client", "iq"), true));
417 DCHECK(!to.empty());
418 root->AddAttr(QName(kEmptyNamespace, "to"), to);
419 if (!from.empty())
420 root->AddAttr(QName(kEmptyNamespace, "from"), from);
421 root->SetAttr(QName(kEmptyNamespace, "type"), "set");
423 XmlElement* jingle_tag =
424 new XmlElement(QName(kJingleNamespace, "jingle"), true);
425 root->AddElement(jingle_tag);
426 jingle_tag->AddAttr(QName(kEmptyNamespace, "sid"), sid);
428 const char* action_attr = ValueToName(kActionTypes, action);
429 if (!action_attr)
430 LOG(FATAL) << "Invalid action value " << action;
431 jingle_tag->AddAttr(QName(kEmptyNamespace, "action"), action_attr);
433 if (action == SESSION_INFO) {
434 if (info.get())
435 jingle_tag->AddElement(new XmlElement(*info.get()));
436 return root.Pass();
439 if (action == SESSION_INITIATE)
440 jingle_tag->AddAttr(QName(kEmptyNamespace, "initiator"), initiator);
442 if (reason != UNKNOWN_REASON) {
443 XmlElement* reason_tag = new XmlElement(QName(kJingleNamespace, "reason"));
444 jingle_tag->AddElement(reason_tag);
445 const char* reason_string =
446 ValueToName(kReasons, reason);
447 if (!reason_string)
448 LOG(FATAL) << "Invalid reason: " << reason;
449 reason_tag->AddElement(new XmlElement(
450 QName(kJingleNamespace, reason_string)));
453 if (action != SESSION_TERMINATE) {
454 XmlElement* content_tag =
455 new XmlElement(QName(kJingleNamespace, "content"));
456 jingle_tag->AddElement(content_tag);
458 content_tag->AddAttr(QName(kEmptyNamespace, "name"),
459 ContentDescription::kChromotingContentName);
460 content_tag->AddAttr(QName(kEmptyNamespace, "creator"), "initiator");
462 if (description.get())
463 content_tag->AddElement(description->ToXml());
465 if (standard_ice) {
466 XmlElement* transport_tag =
467 new XmlElement(QName(kIceTransportNamespace, "transport"), true);
468 content_tag->AddElement(transport_tag);
469 for (std::list<IceCredentials>::const_iterator it =
470 ice_credentials.begin();
471 it != ice_credentials.end(); ++it) {
472 transport_tag->AddElement(FormatIceCredentials(*it));
474 for (std::list<NamedCandidate>::const_iterator it = candidates.begin();
475 it != candidates.end(); ++it) {
476 transport_tag->AddElement(FormatIceCandidate(*it));
478 } else {
479 XmlElement* transport_tag =
480 new XmlElement(QName(kGiceTransportNamespace, "transport"), true);
481 content_tag->AddElement(transport_tag);
482 for (std::list<NamedCandidate>::const_iterator it = candidates.begin();
483 it != candidates.end(); ++it) {
484 transport_tag->AddElement(FormatGiceCandidate(*it));
489 return root.Pass();
492 JingleMessageReply::JingleMessageReply()
493 : type(REPLY_RESULT),
494 error_type(NONE) {
497 JingleMessageReply::JingleMessageReply(ErrorType error)
498 : type(error != NONE ? REPLY_ERROR : REPLY_RESULT),
499 error_type(error) {
502 JingleMessageReply::JingleMessageReply(ErrorType error,
503 const std::string& text_value)
504 : type(REPLY_ERROR),
505 error_type(error),
506 text(text_value) {
509 JingleMessageReply::~JingleMessageReply() { }
511 scoped_ptr<buzz::XmlElement> JingleMessageReply::ToXml(
512 const buzz::XmlElement* request_stanza) const {
513 scoped_ptr<XmlElement> iq(
514 new XmlElement(QName(kJabberNamespace, "iq"), true));
515 iq->SetAttr(QName(kEmptyNamespace, "to"),
516 request_stanza->Attr(QName(kEmptyNamespace, "from")));
517 iq->SetAttr(QName(kEmptyNamespace, "id"),
518 request_stanza->Attr(QName(kEmptyNamespace, "id")));
520 if (type == REPLY_RESULT) {
521 iq->SetAttr(QName(kEmptyNamespace, "type"), "result");
522 return iq.Pass();
525 DCHECK_EQ(type, REPLY_ERROR);
527 iq->SetAttr(QName(kEmptyNamespace, "type"), "error");
529 for (const buzz::XmlElement* child = request_stanza->FirstElement();
530 child != nullptr; child = child->NextElement()) {
531 iq->AddElement(new buzz::XmlElement(*child));
534 buzz::XmlElement* error =
535 new buzz::XmlElement(QName(kJabberNamespace, "error"));
536 iq->AddElement(error);
538 std::string type;
539 std::string error_text;
540 QName name;
541 switch (error_type) {
542 case BAD_REQUEST:
543 type = "modify";
544 name = QName(kJabberNamespace, "bad-request");
545 break;
546 case NOT_IMPLEMENTED:
547 type = "cancel";
548 name = QName(kJabberNamespace, "feature-bad-request");
549 break;
550 case INVALID_SID:
551 type = "modify";
552 name = QName(kJabberNamespace, "item-not-found");
553 error_text = "Invalid SID";
554 break;
555 case UNEXPECTED_REQUEST:
556 type = "modify";
557 name = QName(kJabberNamespace, "unexpected-request");
558 break;
559 case UNSUPPORTED_INFO:
560 type = "modify";
561 name = QName(kJabberNamespace, "feature-not-implemented");
562 break;
563 default:
564 NOTREACHED();
567 if (!text.empty())
568 error_text = text;
570 error->SetAttr(QName(kEmptyNamespace, "type"), type);
572 // If the error name is not in the standard namespace, we have
573 // to first add some error from that namespace.
574 if (name.Namespace() != kJabberNamespace) {
575 error->AddElement(
576 new buzz::XmlElement(QName(kJabberNamespace, "undefined-condition")));
578 error->AddElement(new buzz::XmlElement(name));
580 if (!error_text.empty()) {
581 // It's okay to always use English here. This text is for
582 // debugging purposes only.
583 buzz::XmlElement* text_elem =
584 new buzz::XmlElement(QName(kJabberNamespace, "text"));
585 text_elem->SetAttr(QName(kXmlNamespace, "lang"), "en");
586 text_elem->SetBodyText(error_text);
587 error->AddElement(text_elem);
590 return iq.Pass();
593 } // namespace protocol
594 } // namespace remoting