blob: c2c9b8016e0b7c7fdab9c5deb25e7f4c5ad24c8e [file] [log] [blame]
[email protected]09100e32012-01-03 20:21:211// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]da930e12011-08-19 23:31:432// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/protocol/jingle_messages.h"
6
7#include "base/logging.h"
[email protected]eaf92532013-06-11 07:39:198#include "base/strings/string_number_conversions.h"
[email protected]da930e12011-08-19 23:31:439#include "remoting/base/constants.h"
10#include "remoting/protocol/content_description.h"
[email protected]a5284ef2012-08-15 01:43:0011#include "remoting/protocol/name_value_map.h"
niklaseaf726f12014-09-08 18:43:0212#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
[email protected]da930e12011-08-19 23:31:4313
14using buzz::QName;
15using buzz::XmlElement;
16
17namespace remoting {
18namespace protocol {
19
20const char kJabberNamespace[] = "jabber:client";
21const char kJingleNamespace[] = "urn:xmpp:jingle:1";
22const char kP2PTransportNamespace[] = "http://www.google.com/transport/p2p";
23
24namespace {
25
26const char kEmptyNamespace[] = "";
27const char kXmlNamespace[] = "http://www.w3.org/XML/1998/namespace";
28
[email protected]da930e12011-08-19 23:31:4329const int kPortMin = 1000;
30const int kPortMax = 65535;
31
[email protected]da9ccfb2012-01-28 00:34:4032const NameMapElement<JingleMessage::ActionType> kActionTypes[] = {
[email protected]e3f03d02011-09-27 20:10:5133 { JingleMessage::SESSION_INITIATE, "session-initiate" },
34 { JingleMessage::SESSION_ACCEPT, "session-accept" },
35 { JingleMessage::SESSION_TERMINATE, "session-terminate" },
[email protected]1bc9c7c2011-12-14 00:13:3936 { JingleMessage::SESSION_INFO, "session-info" },
[email protected]e3f03d02011-09-27 20:10:5137 { JingleMessage::TRANSPORT_INFO, "transport-info" },
38};
39
[email protected]da9ccfb2012-01-28 00:34:4040const NameMapElement<JingleMessage::Reason> kReasons[] = {
[email protected]e3f03d02011-09-27 20:10:5141 { JingleMessage::SUCCESS, "success" },
42 { JingleMessage::DECLINE, "decline" },
[email protected]a6b74c212012-03-27 02:38:1143 { JingleMessage::CANCEL, "cancel" },
[email protected]1f3dfe82011-11-15 03:45:4644 { JingleMessage::GENERAL_ERROR, "general-error" },
[email protected]e3f03d02011-09-27 20:10:5145 { JingleMessage::INCOMPATIBLE_PARAMETERS, "incompatible-parameters" },
46};
47
[email protected]da930e12011-08-19 23:31:4348bool ParseCandidate(const buzz::XmlElement* element,
[email protected]8bb746372012-04-26 04:20:1249 JingleMessage::NamedCandidate* candidate) {
[email protected]da930e12011-08-19 23:31:4350 DCHECK(element->Name() == QName(kP2PTransportNamespace, "candidate"));
51
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"));
66
67 int port;
68 double preference;
69 int 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)) {
75 return false;
76 }
77
[email protected]8bb746372012-04-26 04:20:1278 candidate->name = name;
79
[email protected]e758d4c2014-08-06 16:48:1680 candidate->candidate.set_address(rtc::SocketAddress(address, port));
[email protected]8bb746372012-04-26 04:20:1281 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);
[email protected]da930e12011-08-19 23:31:4387
88 return true;
89}
90
[email protected]8bb746372012-04-26 04:20:1291XmlElement* FormatCandidate(const JingleMessage::NamedCandidate& candidate) {
[email protected]da930e12011-08-19 23:31:4392 XmlElement* result =
93 new XmlElement(QName(kP2PTransportNamespace, "candidate"));
[email protected]8bb746372012-04-26 04:20:1294 result->SetAttr(QName(kEmptyNamespace, "name"), candidate.name);
[email protected]da930e12011-08-19 23:31:4395 result->SetAttr(QName(kEmptyNamespace, "address"),
[email protected]71a0c6b2012-05-22 19:49:2196 candidate.candidate.address().ipaddr().ToString());
[email protected]da930e12011-08-19 23:31:4397 result->SetAttr(QName(kEmptyNamespace, "port"),
[email protected]8bb746372012-04-26 04:20:1298 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());
[email protected]da930e12011-08-19 23:31:43106 result->SetAttr(QName(kEmptyNamespace, "preference"),
[email protected]8bb746372012-04-26 04:20:12107 base::DoubleToString(candidate.candidate.preference()));
[email protected]da930e12011-08-19 23:31:43108 result->SetAttr(QName(kEmptyNamespace, "generation"),
[email protected]8bb746372012-04-26 04:20:12109 base::IntToString(candidate.candidate.generation()));
[email protected]da930e12011-08-19 23:31:43110 return result;
111}
112
113} // namespace
114
[email protected]8bb746372012-04-26 04:20:12115JingleMessage::NamedCandidate::NamedCandidate() {
116}
117
118JingleMessage::NamedCandidate::NamedCandidate(
119 const std::string& name,
120 const cricket::Candidate& candidate)
121 : name(name),
122 candidate(candidate) {
123}
124
[email protected]da930e12011-08-19 23:31:43125// static
126bool JingleMessage::IsJingleMessage(const buzz::XmlElement* stanza) {
[email protected]007b3f82013-04-09 08:46:45127 return stanza->Name() == QName(kJabberNamespace, "iq") &&
128 stanza->Attr(QName(std::string(), "type")) == "set" &&
sergeyuc5f104b2015-01-09 19:33:24129 stanza->FirstNamed(QName(kJingleNamespace, "jingle")) != nullptr;
[email protected]da930e12011-08-19 23:31:43130}
131
[email protected]137e7cd2012-02-26 00:28:07132// static
133std::string JingleMessage::GetActionName(ActionType action) {
[email protected]a5284ef2012-08-15 01:43:00134 return ValueToName(kActionTypes, action);
[email protected]137e7cd2012-02-26 00:28:07135}
136
[email protected]da930e12011-08-19 23:31:43137JingleMessage::JingleMessage()
138 : action(UNKNOWN_ACTION),
[email protected]e3f03d02011-09-27 20:10:51139 reason(UNKNOWN_REASON) {
[email protected]da930e12011-08-19 23:31:43140}
141
142JingleMessage::JingleMessage(
143 const std::string& to_value,
144 ActionType action_value,
145 const std::string& sid_value)
146 : to(to_value),
147 action(action_value),
[email protected]aff936c2011-09-30 00:41:01148 sid(sid_value),
149 reason(UNKNOWN_REASON) {
[email protected]da930e12011-08-19 23:31:43150}
151
152JingleMessage::~JingleMessage() {
153}
154
155bool JingleMessage::ParseXml(const buzz::XmlElement* stanza,
156 std::string* error) {
[email protected]09100e32012-01-03 20:21:21157 if (!IsJingleMessage(stanza)) {
158 *error = "Not a jingle message";
159 return false;
160 }
161
[email protected]da930e12011-08-19 23:31:43162 const XmlElement* jingle_tag =
163 stanza->FirstNamed(QName(kJingleNamespace, "jingle"));
[email protected]a5284ef2012-08-15 01:43:00164 if (!jingle_tag) {
[email protected]da930e12011-08-19 23:31:43165 *error = "Not a jingle message";
166 return false;
167 }
168
169 from = stanza->Attr(QName(kEmptyNamespace, "from"));
170 to = stanza->Attr(QName(kEmptyNamespace, "to"));
[email protected]4c3d809e72013-06-21 14:40:40171 initiator = jingle_tag->Attr(QName(kEmptyNamespace, "initiator"));
[email protected]da930e12011-08-19 23:31:43172
173 std::string action_str = jingle_tag->Attr(QName(kEmptyNamespace, "action"));
174 if (action_str.empty()) {
175 *error = "action attribute is missing";
176 return false;
[email protected]e3f03d02011-09-27 20:10:51177 }
[email protected]a5284ef2012-08-15 01:43:00178 if (!NameToValue(kActionTypes, action_str, &action)) {
[email protected]da930e12011-08-19 23:31:43179 *error = "Unknown action " + action_str;
180 return false;
181 }
182
183 sid = jingle_tag->Attr(QName(kEmptyNamespace, "sid"));
184 if (sid.empty()) {
185 *error = "sid attribute is missing";
186 return false;
187 }
188
[email protected]1bc9c7c2011-12-14 00:13:39189 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();
193 if (child) {
194 // session-info is allowed to be empty.
195 info.reset(new XmlElement(*child));
196 } else {
sergeyuc5f104b2015-01-09 19:33:24197 info.reset(nullptr);
[email protected]1bc9c7c2011-12-14 00:13:39198 }
199 return true;
200 }
201
[email protected]e3f03d02011-09-27 20:10:51202 const XmlElement* reason_tag =
203 jingle_tag->FirstNamed(QName(kJingleNamespace, "reason"));
204 if (reason_tag && reason_tag->FirstElement()) {
[email protected]a5284ef2012-08-15 01:43:00205 if (!NameToValue(kReasons, reason_tag->FirstElement()->Name().LocalPart(),
206 &reason)) {
207 reason = UNKNOWN_REASON;
208 }
[email protected]da930e12011-08-19 23:31:43209 }
210
[email protected]e3f03d02011-09-27 20:10:51211 if (action == SESSION_TERMINATE)
212 return true;
213
[email protected]da930e12011-08-19 23:31:43214 const XmlElement* content_tag =
215 jingle_tag->FirstNamed(QName(kJingleNamespace, "content"));
216 if (!content_tag) {
217 *error = "content tag is missing";
218 return false;
219 }
220
221 std::string content_name = content_tag->Attr(QName(kEmptyNamespace, "name"));
222 if (content_name != ContentDescription::kChromotingContentName) {
223 *error = "Unexpected content name: " + content_name;
224 return false;
225 }
226
sergeyuc5f104b2015-01-09 19:33:24227 description.reset(nullptr);
[email protected]da930e12011-08-19 23:31:43228 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";
233 return false;
234 }
235
[email protected]d2de41002012-07-27 00:54:43236 description = ContentDescription::ParseXml(description_tag);
[email protected]da930e12011-08-19 23:31:43237 if (!description.get()) {
238 *error = "Failed to parse content description";
239 return false;
240 }
241 }
242
243 candidates.clear();
244 const XmlElement* transport_tag = content_tag->FirstNamed(
245 QName(kP2PTransportNamespace, "transport"));
246 if (transport_tag) {
247 QName qn_candidate(kP2PTransportNamespace, "candidate");
248 for (const XmlElement* candidate_tag =
249 transport_tag->FirstNamed(qn_candidate);
sergeyuc5f104b2015-01-09 19:33:24250 candidate_tag != nullptr;
[email protected]da930e12011-08-19 23:31:43251 candidate_tag = candidate_tag->NextNamed(qn_candidate)) {
[email protected]8bb746372012-04-26 04:20:12252 NamedCandidate candidate;
[email protected]da930e12011-08-19 23:31:43253 if (!ParseCandidate(candidate_tag, &candidate)) {
254 *error = "Failed to parse candidates";
255 return false;
256 }
257 candidates.push_back(candidate);
258 }
259 }
260
261 return true;
262}
263
[email protected]137e7cd2012-02-26 00:28:07264scoped_ptr<buzz::XmlElement> JingleMessage::ToXml() const {
[email protected]da930e12011-08-19 23:31:43265 scoped_ptr<XmlElement> root(
266 new XmlElement(QName("jabber:client", "iq"), true));
267
268 DCHECK(!to.empty());
269 root->AddAttr(QName(kEmptyNamespace, "to"), to);
270 if (!from.empty())
271 root->AddAttr(QName(kEmptyNamespace, "from"), from);
272 root->SetAttr(QName(kEmptyNamespace, "type"), "set");
273
274 XmlElement* jingle_tag =
275 new XmlElement(QName(kJingleNamespace, "jingle"), true);
276 root->AddElement(jingle_tag);
277 jingle_tag->AddAttr(QName(kEmptyNamespace, "sid"), sid);
278
[email protected]a5284ef2012-08-15 01:43:00279 const char* action_attr = ValueToName(kActionTypes, action);
[email protected]e3f03d02011-09-27 20:10:51280 if (!action_attr)
281 LOG(FATAL) << "Invalid action value " << action;
[email protected]da930e12011-08-19 23:31:43282 jingle_tag->AddAttr(QName(kEmptyNamespace, "action"), action_attr);
283
[email protected]1bc9c7c2011-12-14 00:13:39284 if (action == SESSION_INFO) {
285 if (info.get())
286 jingle_tag->AddElement(new XmlElement(*info.get()));
[email protected]cff27642012-02-23 12:06:19287 return root.Pass();
[email protected]1bc9c7c2011-12-14 00:13:39288 }
289
[email protected]da930e12011-08-19 23:31:43290 if (action == SESSION_INITIATE)
[email protected]4c3d809e72013-06-21 14:40:40291 jingle_tag->AddAttr(QName(kEmptyNamespace, "initiator"), initiator);
[email protected]da930e12011-08-19 23:31:43292
[email protected]e3f03d02011-09-27 20:10:51293 if (reason != UNKNOWN_REASON) {
[email protected]da930e12011-08-19 23:31:43294 XmlElement* reason_tag = new XmlElement(QName(kJingleNamespace, "reason"));
295 jingle_tag->AddElement(reason_tag);
[email protected]e3f03d02011-09-27 20:10:51296 const char* reason_string =
[email protected]a5284ef2012-08-15 01:43:00297 ValueToName(kReasons, reason);
298 if (!reason_string)
[email protected]e3f03d02011-09-27 20:10:51299 LOG(FATAL) << "Invalid reason: " << reason;
300 reason_tag->AddElement(new XmlElement(
301 QName(kJingleNamespace, reason_string)));
302 }
[email protected]da930e12011-08-19 23:31:43303
[email protected]e3f03d02011-09-27 20:10:51304 if (action != SESSION_TERMINATE) {
[email protected]da930e12011-08-19 23:31:43305 XmlElement* content_tag =
306 new XmlElement(QName(kJingleNamespace, "content"));
307 jingle_tag->AddElement(content_tag);
308
309 content_tag->AddAttr(QName(kEmptyNamespace, "name"),
310 ContentDescription::kChromotingContentName);
311 content_tag->AddAttr(QName(kEmptyNamespace, "creator"), "initiator");
312
313 if (description.get())
314 content_tag->AddElement(description->ToXml());
315
316 XmlElement* transport_tag =
317 new XmlElement(QName(kP2PTransportNamespace, "transport"), true);
318 content_tag->AddElement(transport_tag);
[email protected]8bb746372012-04-26 04:20:12319 for (std::list<NamedCandidate>::const_iterator it = candidates.begin();
[email protected]da930e12011-08-19 23:31:43320 it != candidates.end(); ++it) {
321 transport_tag->AddElement(FormatCandidate(*it));
322 }
323 }
324
[email protected]cff27642012-02-23 12:06:19325 return root.Pass();
[email protected]da930e12011-08-19 23:31:43326}
327
328JingleMessageReply::JingleMessageReply()
329 : type(REPLY_RESULT),
330 error_type(NONE) {
331}
332
333JingleMessageReply::JingleMessageReply(ErrorType error)
[email protected]b0fffb02012-02-17 21:59:43334 : type(error != NONE ? REPLY_ERROR : REPLY_RESULT),
[email protected]da930e12011-08-19 23:31:43335 error_type(error) {
336}
337
338JingleMessageReply::JingleMessageReply(ErrorType error,
339 const std::string& text_value)
340 : type(REPLY_ERROR),
341 error_type(error),
342 text(text_value) {
343}
344
345JingleMessageReply::~JingleMessageReply() { }
346
[email protected]cff27642012-02-23 12:06:19347scoped_ptr<buzz::XmlElement> JingleMessageReply::ToXml(
[email protected]da930e12011-08-19 23:31:43348 const buzz::XmlElement* request_stanza) const {
[email protected]cff27642012-02-23 12:06:19349 scoped_ptr<XmlElement> iq(
350 new XmlElement(QName(kJabberNamespace, "iq"), true));
[email protected]da930e12011-08-19 23:31:43351 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")));
355
356 if (type == REPLY_RESULT) {
357 iq->SetAttr(QName(kEmptyNamespace, "type"), "result");
[email protected]cff27642012-02-23 12:06:19358 return iq.Pass();
[email protected]da930e12011-08-19 23:31:43359 }
360
361 DCHECK_EQ(type, REPLY_ERROR);
362
363 iq->SetAttr(QName(kEmptyNamespace, "type"), "error");
364
365 for (const buzz::XmlElement* child = request_stanza->FirstElement();
sergeyuc5f104b2015-01-09 19:33:24366 child != nullptr; child = child->NextElement()) {
[email protected]da930e12011-08-19 23:31:43367 iq->AddElement(new buzz::XmlElement(*child));
368 }
369
370 buzz::XmlElement* error =
371 new buzz::XmlElement(QName(kJabberNamespace, "error"));
372 iq->AddElement(error);
373
374 std::string type;
375 std::string error_text;
[email protected]007b3f82013-04-09 08:46:45376 QName name;
[email protected]da930e12011-08-19 23:31:43377 switch (error_type) {
378 case BAD_REQUEST:
379 type = "modify";
380 name = QName(kJabberNamespace, "bad-request");
381 break;
382 case NOT_IMPLEMENTED:
383 type = "cancel";
384 name = QName(kJabberNamespace, "feature-bad-request");
385 break;
386 case INVALID_SID:
387 type = "modify";
388 name = QName(kJabberNamespace, "item-not-found");
389 error_text = "Invalid SID";
390 break;
391 case UNEXPECTED_REQUEST:
392 type = "modify";
393 name = QName(kJabberNamespace, "unexpected-request");
394 break;
[email protected]1bc9c7c2011-12-14 00:13:39395 case UNSUPPORTED_INFO:
396 type = "modify";
397 name = QName(kJabberNamespace, "feature-not-implemented");
398 break;
[email protected]da930e12011-08-19 23:31:43399 default:
400 NOTREACHED();
401 }
402
403 if (!text.empty())
404 error_text = text;
405
406 error->SetAttr(QName(kEmptyNamespace, "type"), type);
407
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) {
411 error->AddElement(
412 new buzz::XmlElement(QName(kJabberNamespace, "undefined-condition")));
413 }
414 error->AddElement(new buzz::XmlElement(name));
415
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);
424 }
425
[email protected]cff27642012-02-23 12:06:19426 return iq.Pass();
[email protected]da930e12011-08-19 23:31:43427}
428
429} // namespace protocol
430} // namespace remoting