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.
4
5#include "remoting/protocol/jingle_messages.h"
6
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"
13
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
29const int kPortMin = 1000;
30const int kPortMax = 65535;
31
32const 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" },
38};
39
40const 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" },
46};
47
48bool ParseCandidate(const buzz::XmlElement* element,
49                    JingleMessage::NamedCandidate* candidate) {
50  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
78  candidate->name = name;
79
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);
87
88  return true;
89}
90
91XmlElement* FormatCandidate(const JingleMessage::NamedCandidate& candidate) {
92  XmlElement* result =
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()));
110  return result;
111}
112
113}  // namespace
114
115JingleMessage::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
125// static
126bool 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")) != NULL;
130}
131
132// static
133std::string JingleMessage::GetActionName(ActionType action) {
134  return ValueToName(kActionTypes, action);
135}
136
137JingleMessage::JingleMessage()
138    : action(UNKNOWN_ACTION),
139      reason(UNKNOWN_REASON) {
140}
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),
148      sid(sid_value),
149      reason(UNKNOWN_REASON) {
150}
151
152JingleMessage::~JingleMessage() {
153}
154
155bool JingleMessage::ParseXml(const buzz::XmlElement* stanza,
156                             std::string* error) {
157  if (!IsJingleMessage(stanza)) {
158    *error = "Not a jingle message";
159    return false;
160  }
161
162  const XmlElement* jingle_tag =
163      stanza->FirstNamed(QName(kJingleNamespace, "jingle"));
164  if (!jingle_tag) {
165    *error = "Not a jingle message";
166    return false;
167  }
168
169  from = stanza->Attr(QName(kEmptyNamespace, "from"));
170  to = stanza->Attr(QName(kEmptyNamespace, "to"));
171  initiator = jingle_tag->Attr(QName(kEmptyNamespace, "initiator"));
172
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;
177  }
178  if (!NameToValue(kActionTypes, action_str, &action)) {
179    *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
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();
193    if (child) {
194      // session-info is allowed to be empty.
195      info.reset(new XmlElement(*child));
196    } else {
197      info.reset(NULL);
198    }
199    return true;
200  }
201
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(),
206                     &reason)) {
207      reason = UNKNOWN_REASON;
208    }
209  }
210
211  if (action == SESSION_TERMINATE)
212    return true;
213
214  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
227  description.reset(NULL);
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";
233      return false;
234    }
235
236    description = ContentDescription::ParseXml(description_tag);
237    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);
250         candidate_tag != NULL;
251         candidate_tag = candidate_tag->NextNamed(qn_candidate)) {
252      NamedCandidate candidate;
253      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
264scoped_ptr<buzz::XmlElement> JingleMessage::ToXml() const {
265  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
279  const char* action_attr = ValueToName(kActionTypes, action);
280  if (!action_attr)
281    LOG(FATAL) << "Invalid action value " << action;
282  jingle_tag->AddAttr(QName(kEmptyNamespace, "action"), action_attr);
283
284  if (action == SESSION_INFO) {
285    if (info.get())
286      jingle_tag->AddElement(new XmlElement(*info.get()));
287    return root.Pass();
288  }
289
290  if (action == SESSION_INITIATE)
291    jingle_tag->AddAttr(QName(kEmptyNamespace, "initiator"), initiator);
292
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);
298    if (!reason_string)
299      LOG(FATAL) << "Invalid reason: " << reason;
300    reason_tag->AddElement(new XmlElement(
301        QName(kJingleNamespace, reason_string)));
302  }
303
304  if (action != SESSION_TERMINATE) {
305    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);
319    for (std::list<NamedCandidate>::const_iterator it = candidates.begin();
320         it != candidates.end(); ++it) {
321      transport_tag->AddElement(FormatCandidate(*it));
322    }
323  }
324
325  return root.Pass();
326}
327
328JingleMessageReply::JingleMessageReply()
329    : type(REPLY_RESULT),
330      error_type(NONE) {
331}
332
333JingleMessageReply::JingleMessageReply(ErrorType error)
334    : type(error != NONE ? REPLY_ERROR : REPLY_RESULT),
335      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
347scoped_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")));
355
356  if (type == REPLY_RESULT) {
357    iq->SetAttr(QName(kEmptyNamespace, "type"), "result");
358    return iq.Pass();
359  }
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();
366       child != NULL; child = child->NextElement()) {
367    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;
376  QName name;
377  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;
395    case UNSUPPORTED_INFO:
396      type = "modify";
397      name = QName(kJabberNamespace, "feature-not-implemented");
398      break;
399    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
426  return iq.Pass();
427}
428
429}  // namespace protocol
430}  // namespace remoting
431