• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
14 using buzz::QName;
15 using buzz::XmlElement;
16 
17 namespace remoting {
18 namespace protocol {
19 
20 const char kJabberNamespace[] = "jabber:client";
21 const char kJingleNamespace[] = "urn:xmpp:jingle:1";
22 const char kP2PTransportNamespace[] = "http://www.google.com/transport/p2p";
23 
24 namespace {
25 
26 const char kEmptyNamespace[] = "";
27 const char kXmlNamespace[] = "http://www.w3.org/XML/1998/namespace";
28 
29 const int kPortMin = 1000;
30 const int kPortMax = 65535;
31 
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" },
38 };
39 
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" },
46 };
47 
ParseCandidate(const buzz::XmlElement * element,JingleMessage::NamedCandidate * candidate)48 bool 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 
FormatCandidate(const JingleMessage::NamedCandidate & candidate)91 XmlElement* 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 
NamedCandidate()115 JingleMessage::NamedCandidate::NamedCandidate() {
116 }
117 
NamedCandidate(const std::string & name,const cricket::Candidate & candidate)118 JingleMessage::NamedCandidate::NamedCandidate(
119     const std::string& name,
120     const cricket::Candidate& candidate)
121     : name(name),
122       candidate(candidate) {
123 }
124 
125 // static
IsJingleMessage(const buzz::XmlElement * stanza)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")) != NULL;
130 }
131 
132 // static
GetActionName(ActionType action)133 std::string JingleMessage::GetActionName(ActionType action) {
134   return ValueToName(kActionTypes, action);
135 }
136 
JingleMessage()137 JingleMessage::JingleMessage()
138     : action(UNKNOWN_ACTION),
139       reason(UNKNOWN_REASON) {
140 }
141 
JingleMessage(const std::string & to_value,ActionType action_value,const std::string & sid_value)142 JingleMessage::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 
~JingleMessage()152 JingleMessage::~JingleMessage() {
153 }
154 
ParseXml(const buzz::XmlElement * stanza,std::string * error)155 bool 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 
ToXml() const264 scoped_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 
JingleMessageReply()328 JingleMessageReply::JingleMessageReply()
329     : type(REPLY_RESULT),
330       error_type(NONE) {
331 }
332 
JingleMessageReply(ErrorType error)333 JingleMessageReply::JingleMessageReply(ErrorType error)
334     : type(error != NONE ? REPLY_ERROR : REPLY_RESULT),
335       error_type(error) {
336 }
337 
JingleMessageReply(ErrorType error,const std::string & text_value)338 JingleMessageReply::JingleMessageReply(ErrorType error,
339                                        const std::string& text_value)
340     : type(REPLY_ERROR),
341       error_type(error),
342       text(text_value) {
343 }
344 
~JingleMessageReply()345 JingleMessageReply::~JingleMessageReply() { }
346 
ToXml(const buzz::XmlElement * request_stanza) const347 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")));
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