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 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 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 115 JingleMessage::NamedCandidate::NamedCandidate() { 116 } 117 118 JingleMessage::NamedCandidate::NamedCandidate( 119 const std::string& name, 120 const cricket::Candidate& candidate) 121 : name(name), 122 candidate(candidate) { 123 } 124 125 // static 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 133 std::string JingleMessage::GetActionName(ActionType action) { 134 return ValueToName(kActionTypes, action); 135 } 136 137 JingleMessage::JingleMessage() 138 : action(UNKNOWN_ACTION), 139 reason(UNKNOWN_REASON) { 140 } 141 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 152 JingleMessage::~JingleMessage() { 153 } 154 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 264 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 328 JingleMessageReply::JingleMessageReply() 329 : type(REPLY_RESULT), 330 error_type(NONE) { 331 } 332 333 JingleMessageReply::JingleMessageReply(ErrorType error) 334 : type(error != NONE ? REPLY_ERROR : REPLY_RESULT), 335 error_type(error) { 336 } 337 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 345 JingleMessageReply::~JingleMessageReply() { } 346 347 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