1 /* 2 * libjingle 3 * Copyright 2010, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <stdio.h> 29 #include <string> 30 #include "talk/p2p/base/sessionmessages.h" 31 32 #include "talk/base/logging.h" 33 #include "talk/base/scoped_ptr.h" 34 #include "talk/base/stringutils.h" 35 #include "talk/xmllite/xmlconstants.h" 36 #include "talk/xmpp/constants.h" 37 #include "talk/p2p/base/constants.h" 38 #include "talk/p2p/base/p2ptransport.h" 39 #include "talk/p2p/base/parsing.h" 40 #include "talk/p2p/base/sessionclient.h" 41 #include "talk/p2p/base/sessiondescription.h" 42 #include "talk/p2p/base/transport.h" 43 #include "talk/xmllite/xmlconstants.h" 44 45 namespace cricket { 46 47 ActionType ToActionType(const std::string& type) { 48 if (type == GINGLE_ACTION_INITIATE) 49 return ACTION_SESSION_INITIATE; 50 if (type == GINGLE_ACTION_INFO) 51 return ACTION_SESSION_INFO; 52 if (type == GINGLE_ACTION_ACCEPT) 53 return ACTION_SESSION_ACCEPT; 54 if (type == GINGLE_ACTION_REJECT) 55 return ACTION_SESSION_REJECT; 56 if (type == GINGLE_ACTION_TERMINATE) 57 return ACTION_SESSION_TERMINATE; 58 if (type == GINGLE_ACTION_CANDIDATES) 59 return ACTION_TRANSPORT_INFO; 60 if (type == JINGLE_ACTION_SESSION_INITIATE) 61 return ACTION_SESSION_INITIATE; 62 if (type == JINGLE_ACTION_TRANSPORT_INFO) 63 return ACTION_TRANSPORT_INFO; 64 if (type == JINGLE_ACTION_TRANSPORT_ACCEPT) 65 return ACTION_TRANSPORT_ACCEPT; 66 if (type == JINGLE_ACTION_SESSION_INFO) 67 return ACTION_SESSION_INFO; 68 if (type == JINGLE_ACTION_SESSION_ACCEPT) 69 return ACTION_SESSION_ACCEPT; 70 if (type == JINGLE_ACTION_SESSION_TERMINATE) 71 return ACTION_SESSION_TERMINATE; 72 if (type == JINGLE_ACTION_TRANSPORT_INFO) 73 return ACTION_TRANSPORT_INFO; 74 if (type == JINGLE_ACTION_TRANSPORT_ACCEPT) 75 return ACTION_TRANSPORT_ACCEPT; 76 if (type == GINGLE_ACTION_NOTIFY) 77 return ACTION_NOTIFY; 78 if (type == GINGLE_ACTION_UPDATE) 79 return ACTION_UPDATE; 80 81 return ACTION_UNKNOWN; 82 } 83 84 std::string ToJingleString(ActionType type) { 85 switch (type) { 86 case ACTION_SESSION_INITIATE: 87 return JINGLE_ACTION_SESSION_INITIATE; 88 case ACTION_SESSION_INFO: 89 return JINGLE_ACTION_SESSION_INFO; 90 case ACTION_SESSION_ACCEPT: 91 return JINGLE_ACTION_SESSION_ACCEPT; 92 // Notice that reject and terminate both go to 93 // "session-terminate", but there is no "session-reject". 94 case ACTION_SESSION_REJECT: 95 case ACTION_SESSION_TERMINATE: 96 return JINGLE_ACTION_SESSION_TERMINATE; 97 case ACTION_TRANSPORT_INFO: 98 return JINGLE_ACTION_TRANSPORT_INFO; 99 case ACTION_TRANSPORT_ACCEPT: 100 return JINGLE_ACTION_TRANSPORT_ACCEPT; 101 default: 102 return ""; 103 } 104 } 105 106 std::string ToGingleString(ActionType type) { 107 switch (type) { 108 case ACTION_SESSION_INITIATE: 109 return GINGLE_ACTION_INITIATE; 110 case ACTION_SESSION_INFO: 111 return GINGLE_ACTION_INFO; 112 case ACTION_SESSION_ACCEPT: 113 return GINGLE_ACTION_ACCEPT; 114 case ACTION_SESSION_REJECT: 115 return GINGLE_ACTION_REJECT; 116 case ACTION_SESSION_TERMINATE: 117 return GINGLE_ACTION_TERMINATE; 118 case ACTION_VIEW: 119 return GINGLE_ACTION_VIEW; 120 case ACTION_TRANSPORT_INFO: 121 return GINGLE_ACTION_CANDIDATES; 122 default: 123 return ""; 124 } 125 } 126 127 128 bool IsJingleMessage(const buzz::XmlElement* stanza) { 129 const buzz::XmlElement* jingle = stanza->FirstNamed(QN_JINGLE); 130 if (jingle == NULL) 131 return false; 132 133 return (jingle->HasAttr(buzz::QN_ACTION) && 134 (jingle->HasAttr(QN_SID) 135 // TODO: This works around a bug in old jingle 136 // clients that set QN_ID instead of QN_SID. Once we know 137 // there are no clients which have this bug, we can remove 138 // this code. 139 || jingle->HasAttr(QN_ID))); 140 } 141 142 bool IsGingleMessage(const buzz::XmlElement* stanza) { 143 const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION); 144 if (session == NULL) 145 return false; 146 147 return (session->HasAttr(buzz::QN_TYPE) && 148 session->HasAttr(buzz::QN_ID) && 149 session->HasAttr(QN_INITIATOR)); 150 } 151 152 bool IsSessionMessage(const buzz::XmlElement* stanza) { 153 return (stanza->Name() == buzz::QN_IQ && 154 stanza->Attr(buzz::QN_TYPE) == buzz::STR_SET && 155 (IsJingleMessage(stanza) || 156 IsGingleMessage(stanza))); 157 } 158 159 bool ParseGingleSessionMessage(const buzz::XmlElement* session, 160 SessionMessage* msg, 161 ParseError* error) { 162 msg->protocol = PROTOCOL_GINGLE; 163 std::string type_string = session->Attr(buzz::QN_TYPE); 164 msg->type = ToActionType(type_string); 165 msg->sid = session->Attr(buzz::QN_ID); 166 msg->initiator = session->Attr(QN_INITIATOR); 167 msg->action_elem = session; 168 169 if (msg->type == ACTION_UNKNOWN) 170 return BadParse("unknown action: " + type_string, error); 171 172 return true; 173 } 174 175 bool ParseJingleSessionMessage(const buzz::XmlElement* jingle, 176 SessionMessage* msg, 177 ParseError* error) { 178 msg->protocol = PROTOCOL_JINGLE; 179 std::string type_string = jingle->Attr(buzz::QN_ACTION); 180 msg->type = ToActionType(type_string); 181 msg->sid = jingle->Attr(QN_SID); 182 // TODO: This works around a bug in old jingle clients 183 // that set QN_ID instead of QN_SID. Once we know there are no 184 // clients which have this bug, we can remove this code. 185 if (msg->sid.empty()) { 186 msg->sid = jingle->Attr(buzz::QN_ID); 187 } 188 msg->initiator = GetXmlAttr(jingle, QN_INITIATOR, buzz::STR_EMPTY); 189 msg->action_elem = jingle; 190 191 if (msg->type == ACTION_UNKNOWN) 192 return BadParse("unknown action: " + type_string, error); 193 194 return true; 195 } 196 197 bool ParseHybridSessionMessage(const buzz::XmlElement* jingle, 198 SessionMessage* msg, 199 ParseError* error) { 200 if (!ParseJingleSessionMessage(jingle, msg, error)) 201 return false; 202 msg->protocol = PROTOCOL_HYBRID; 203 204 return true; 205 } 206 207 bool ParseSessionMessage(const buzz::XmlElement* stanza, 208 SessionMessage* msg, 209 ParseError* error) { 210 msg->id = stanza->Attr(buzz::QN_ID); 211 msg->from = stanza->Attr(buzz::QN_FROM); 212 msg->to = stanza->Attr(buzz::QN_TO); 213 msg->stanza = stanza; 214 215 const buzz::XmlElement* jingle = stanza->FirstNamed(QN_JINGLE); 216 const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION); 217 if (jingle && session) 218 return ParseHybridSessionMessage(jingle, msg, error); 219 if (jingle != NULL) 220 return ParseJingleSessionMessage(jingle, msg, error); 221 if (session != NULL) 222 return ParseGingleSessionMessage(session, msg, error); 223 return false; 224 } 225 226 buzz::XmlElement* WriteGingleAction(const SessionMessage& msg, 227 const XmlElements& action_elems) { 228 buzz::XmlElement* session = new buzz::XmlElement(QN_GINGLE_SESSION, true); 229 session->AddAttr(buzz::QN_TYPE, ToGingleString(msg.type)); 230 session->AddAttr(buzz::QN_ID, msg.sid); 231 session->AddAttr(QN_INITIATOR, msg.initiator); 232 AddXmlChildren(session, action_elems); 233 return session; 234 } 235 236 buzz::XmlElement* WriteJingleAction(const SessionMessage& msg, 237 const XmlElements& action_elems) { 238 buzz::XmlElement* jingle = new buzz::XmlElement(QN_JINGLE, true); 239 jingle->AddAttr(buzz::QN_ACTION, ToJingleString(msg.type)); 240 jingle->AddAttr(QN_SID, msg.sid); 241 // TODO: This works around a bug in old jingle clinets 242 // that expected QN_ID instead of QN_SID. Once we know there are no 243 // clients which have this bug, we can remove this code. 244 jingle->AddAttr(QN_ID, msg.sid); 245 // TODO: Right now, the XMPP server rejects a jingle-only 246 // (non hybrid) message with "feature-not-implemented" if there is 247 // no initiator. Fix the server, and then only set the initiator on 248 // session-initiate messages here. 249 jingle->AddAttr(QN_INITIATOR, msg.initiator); 250 AddXmlChildren(jingle, action_elems); 251 return jingle; 252 } 253 254 void WriteSessionMessage(const SessionMessage& msg, 255 const XmlElements& action_elems, 256 buzz::XmlElement* stanza) { 257 stanza->SetAttr(buzz::QN_TO, msg.to); 258 stanza->SetAttr(buzz::QN_TYPE, buzz::STR_SET); 259 260 if (msg.protocol == PROTOCOL_GINGLE) { 261 stanza->AddElement(WriteGingleAction(msg, action_elems)); 262 } else { 263 stanza->AddElement(WriteJingleAction(msg, action_elems)); 264 } 265 } 266 267 268 TransportParser* GetTransportParser(const TransportParserMap& trans_parsers, 269 const std::string& name) { 270 TransportParserMap::const_iterator map = trans_parsers.find(name); 271 if (map == trans_parsers.end()) { 272 return NULL; 273 } else { 274 return map->second; 275 } 276 } 277 278 bool ParseCandidates(SignalingProtocol protocol, 279 const buzz::XmlElement* candidates_elem, 280 const TransportParserMap& trans_parsers, 281 const std::string& transport_type, 282 Candidates* candidates, 283 ParseError* error) { 284 TransportParser* trans_parser = 285 GetTransportParser(trans_parsers, transport_type); 286 if (trans_parser == NULL) 287 return BadParse("unknown transport type: " + transport_type, error); 288 289 return trans_parser->ParseCandidates(protocol, candidates_elem, 290 candidates, error); 291 } 292 293 bool ParseGingleTransportInfos(const buzz::XmlElement* action_elem, 294 const ContentInfos& contents, 295 const TransportParserMap& trans_parsers, 296 TransportInfos* tinfos, 297 ParseError* error) { 298 TransportInfo tinfo(CN_OTHER, NS_GINGLE_P2P, Candidates()); 299 if (!ParseCandidates(PROTOCOL_GINGLE, action_elem, 300 trans_parsers, NS_GINGLE_P2P, 301 &tinfo.candidates, error)) 302 return false; 303 304 bool has_audio = FindContentInfoByName(contents, CN_AUDIO) != NULL; 305 bool has_video = FindContentInfoByName(contents, CN_VIDEO) != NULL; 306 307 // If we don't have media, no need to separate the candidates. 308 if (!has_audio && !has_audio) { 309 tinfos->push_back(tinfo); 310 return true; 311 } 312 313 // If we have media, separate the candidates. Create the 314 // TransportInfo here to avoid copying the candidates. 315 TransportInfo audio_tinfo(CN_AUDIO, NS_GINGLE_P2P, Candidates()); 316 TransportInfo video_tinfo(CN_VIDEO, NS_GINGLE_P2P, Candidates()); 317 for (Candidates::iterator cand = tinfo.candidates.begin(); 318 cand != tinfo.candidates.end(); cand++) { 319 if (cand->name() == GINGLE_CANDIDATE_NAME_RTP || 320 cand->name() == GINGLE_CANDIDATE_NAME_RTCP) { 321 audio_tinfo.candidates.push_back(*cand); 322 } else if (cand->name() == GINGLE_CANDIDATE_NAME_VIDEO_RTP || 323 cand->name() == GINGLE_CANDIDATE_NAME_VIDEO_RTCP) { 324 video_tinfo.candidates.push_back(*cand); 325 } 326 } 327 328 if (has_audio) { 329 tinfos->push_back(audio_tinfo); 330 } 331 332 if (has_video) { 333 tinfos->push_back(video_tinfo); 334 } 335 336 return true; 337 } 338 339 bool ParseJingleTransportInfo(const buzz::XmlElement* trans_elem, 340 const ContentInfo& content, 341 const TransportParserMap& trans_parsers, 342 TransportInfos* tinfos, 343 ParseError* error) { 344 std::string transport_type = trans_elem->Name().Namespace(); 345 TransportInfo tinfo(content.name, transport_type, Candidates()); 346 if (!ParseCandidates(PROTOCOL_JINGLE, trans_elem, 347 trans_parsers, transport_type, 348 &tinfo.candidates, error)) 349 return false; 350 351 tinfos->push_back(tinfo); 352 return true; 353 } 354 355 bool ParseJingleTransportInfos(const buzz::XmlElement* jingle, 356 const ContentInfos& contents, 357 const TransportParserMap trans_parsers, 358 TransportInfos* tinfos, 359 ParseError* error) { 360 for (const buzz::XmlElement* pair_elem 361 = jingle->FirstNamed(QN_JINGLE_CONTENT); 362 pair_elem != NULL; 363 pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) { 364 std::string content_name; 365 if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME, 366 &content_name, error)) 367 return false; 368 369 const ContentInfo* content = FindContentInfoByName(contents, content_name); 370 if (!content) 371 return BadParse("Unknown content name: " + content_name, error); 372 373 const buzz::XmlElement* trans_elem; 374 if (!RequireXmlChild(pair_elem, LN_TRANSPORT, &trans_elem, error)) 375 return false; 376 377 if (!ParseJingleTransportInfo(trans_elem, *content, trans_parsers, 378 tinfos, error)) 379 return false; 380 } 381 382 return true; 383 } 384 385 buzz::XmlElement* NewTransportElement(const std::string& name) { 386 return new buzz::XmlElement(buzz::QName(true, name, LN_TRANSPORT), true); 387 } 388 389 bool WriteCandidates(SignalingProtocol protocol, 390 const std::string& trans_type, 391 const Candidates& candidates, 392 const TransportParserMap& trans_parsers, 393 XmlElements* elems, 394 WriteError* error) { 395 TransportParser* trans_parser = GetTransportParser(trans_parsers, trans_type); 396 if (trans_parser == NULL) 397 return BadWrite("unknown transport type: " + trans_type, error); 398 399 return trans_parser->WriteCandidates(protocol, candidates, elems, error); 400 } 401 402 bool WriteGingleTransportInfos(const TransportInfos& tinfos, 403 const TransportParserMap& trans_parsers, 404 XmlElements* elems, 405 WriteError* error) { 406 for (TransportInfos::const_iterator tinfo = tinfos.begin(); 407 tinfo != tinfos.end(); ++tinfo) { 408 if (!WriteCandidates(PROTOCOL_GINGLE, 409 tinfo->transport_type, tinfo->candidates, 410 trans_parsers, elems, error)) 411 return false; 412 } 413 414 return true; 415 } 416 417 bool WriteJingleTransportInfo(const TransportInfo& tinfo, 418 const TransportParserMap& trans_parsers, 419 XmlElements* elems, 420 WriteError* error) { 421 XmlElements candidate_elems; 422 if (!WriteCandidates(PROTOCOL_JINGLE, 423 tinfo.transport_type, tinfo.candidates, trans_parsers, 424 &candidate_elems, error)) 425 return false; 426 427 buzz::XmlElement* trans_elem = NewTransportElement(tinfo.transport_type); 428 AddXmlChildren(trans_elem, candidate_elems); 429 elems->push_back(trans_elem); 430 return true; 431 } 432 433 void WriteJingleContentPair(const std::string name, 434 const XmlElements& pair_elems, 435 XmlElements* elems) { 436 buzz::XmlElement* pair_elem = new buzz::XmlElement(QN_JINGLE_CONTENT); 437 pair_elem->SetAttr(QN_JINGLE_CONTENT_NAME, name); 438 pair_elem->SetAttr(QN_CREATOR, LN_INITIATOR); 439 AddXmlChildren(pair_elem, pair_elems); 440 441 elems->push_back(pair_elem); 442 } 443 444 bool WriteJingleTransportInfos(const TransportInfos& tinfos, 445 const TransportParserMap& trans_parsers, 446 XmlElements* elems, 447 WriteError* error) { 448 for (TransportInfos::const_iterator tinfo = tinfos.begin(); 449 tinfo != tinfos.end(); ++tinfo) { 450 XmlElements pair_elems; 451 if (!WriteJingleTransportInfo(*tinfo, trans_parsers, 452 &pair_elems, error)) 453 return false; 454 455 WriteJingleContentPair(tinfo->content_name, pair_elems, elems); 456 } 457 458 return true; 459 } 460 461 ContentParser* GetContentParser(const ContentParserMap& content_parsers, 462 const std::string& type) { 463 ContentParserMap::const_iterator map = content_parsers.find(type); 464 if (map == content_parsers.end()) { 465 return NULL; 466 } else { 467 return map->second; 468 } 469 } 470 471 bool ParseContentInfo(SignalingProtocol protocol, 472 const std::string& name, 473 const std::string& type, 474 const buzz::XmlElement* elem, 475 const ContentParserMap& parsers, 476 ContentInfos* contents, 477 ParseError* error) { 478 ContentParser* parser = GetContentParser(parsers, type); 479 if (parser == NULL) 480 return BadParse("unknown application content: " + type, error); 481 482 const ContentDescription* desc; 483 if (!parser->ParseContent(protocol, elem, &desc, error)) 484 return false; 485 486 contents->push_back(ContentInfo(name, type, desc)); 487 return true; 488 } 489 490 bool ParseContentType(const buzz::XmlElement* parent_elem, 491 std::string* content_type, 492 const buzz::XmlElement** content_elem, 493 ParseError* error) { 494 if (!RequireXmlChild(parent_elem, LN_DESCRIPTION, content_elem, error)) 495 return false; 496 497 *content_type = (*content_elem)->Name().Namespace(); 498 return true; 499 } 500 501 bool ParseGingleContentInfos(const buzz::XmlElement* session, 502 const ContentParserMap& content_parsers, 503 ContentInfos* contents, 504 ParseError* error) { 505 std::string content_type; 506 const buzz::XmlElement* content_elem; 507 if (!ParseContentType(session, &content_type, &content_elem, error)) 508 return false; 509 510 if (content_type == NS_GINGLE_VIDEO) { 511 // A parser parsing audio or video content should look at the 512 // namespace and only parse the codecs relevant to that namespace. 513 // We use this to control which codecs get parsed: first audio, 514 // then video. 515 talk_base::scoped_ptr<buzz::XmlElement> audio_elem( 516 new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT)); 517 CopyXmlChildren(content_elem, audio_elem.get()); 518 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP, 519 audio_elem.get(), content_parsers, 520 contents, error)) 521 return false; 522 523 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_VIDEO, NS_JINGLE_RTP, 524 content_elem, content_parsers, 525 contents, error)) 526 return false; 527 } else if (content_type == NS_GINGLE_AUDIO) { 528 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP, 529 content_elem, content_parsers, 530 contents, error)) 531 return false; 532 } else { 533 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_OTHER, content_type, 534 content_elem, content_parsers, 535 contents, error)) 536 return false; 537 } 538 return true; 539 } 540 541 bool ParseJingleContentInfos(const buzz::XmlElement* jingle, 542 const ContentParserMap& content_parsers, 543 ContentInfos* contents, 544 ParseError* error) { 545 for (const buzz::XmlElement* pair_elem 546 = jingle->FirstNamed(QN_JINGLE_CONTENT); 547 pair_elem != NULL; 548 pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) { 549 std::string content_name; 550 if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME, 551 &content_name, error)) 552 return false; 553 554 std::string content_type; 555 const buzz::XmlElement* content_elem; 556 if (!ParseContentType(pair_elem, &content_type, &content_elem, error)) 557 return false; 558 559 if (!ParseContentInfo(PROTOCOL_JINGLE, content_name, content_type, 560 content_elem, content_parsers, 561 contents, error)) 562 return false; 563 } 564 return true; 565 } 566 567 buzz::XmlElement* WriteContentInfo(SignalingProtocol protocol, 568 const ContentInfo& content, 569 const ContentParserMap& parsers, 570 WriteError* error) { 571 ContentParser* parser = GetContentParser(parsers, content.type); 572 if (parser == NULL) { 573 BadWrite("unknown content type: " + content.type, error); 574 return NULL; 575 } 576 577 buzz::XmlElement* elem = NULL; 578 if (!parser->WriteContent(protocol, content.description, &elem, error)) 579 return NULL; 580 581 return elem; 582 } 583 584 bool WriteGingleContentInfos(const ContentInfos& contents, 585 const ContentParserMap& parsers, 586 XmlElements* elems, 587 WriteError* error) { 588 if (contents.size() == 1) { 589 buzz::XmlElement* elem = WriteContentInfo( 590 PROTOCOL_GINGLE, contents.front(), parsers, error); 591 if (!elem) 592 return false; 593 594 elems->push_back(elem); 595 } else if (contents.size() == 2 && 596 contents.at(0).type == NS_JINGLE_RTP && 597 contents.at(1).type == NS_JINGLE_RTP) { 598 // Special-case audio + video contents so that they are "merged" 599 // into one "video" content. 600 buzz::XmlElement* audio = WriteContentInfo( 601 PROTOCOL_GINGLE, contents.at(0), parsers, error); 602 if (!audio) 603 return false; 604 605 buzz::XmlElement* video = WriteContentInfo( 606 PROTOCOL_GINGLE, contents.at(1), parsers, error); 607 if (!video) { 608 delete audio; 609 return false; 610 } 611 612 CopyXmlChildren(audio, video); 613 elems->push_back(video); 614 delete audio; 615 } else { 616 return BadWrite("Gingle protocol may only have one content.", error); 617 } 618 619 return true; 620 } 621 622 const TransportInfo* GetTransportInfoByContentName( 623 const TransportInfos& tinfos, const std::string& content_name) { 624 for (TransportInfos::const_iterator tinfo = tinfos.begin(); 625 tinfo != tinfos.end(); ++tinfo) { 626 if (content_name == tinfo->content_name) { 627 return &*tinfo; 628 } 629 } 630 return NULL; 631 } 632 633 bool WriteJingleContentPairs(const ContentInfos& contents, 634 const ContentParserMap& content_parsers, 635 const TransportInfos& tinfos, 636 const TransportParserMap& trans_parsers, 637 XmlElements* elems, 638 WriteError* error) { 639 for (ContentInfos::const_iterator content = contents.begin(); 640 content != contents.end(); ++content) { 641 const TransportInfo* tinfo = 642 GetTransportInfoByContentName(tinfos, content->name); 643 if (!tinfo) 644 return BadWrite("No transport for content: " + content->name, error); 645 646 XmlElements pair_elems; 647 buzz::XmlElement* elem = WriteContentInfo( 648 PROTOCOL_JINGLE, *content, content_parsers, error); 649 if (!elem) 650 return false; 651 pair_elems.push_back(elem); 652 653 if (!WriteJingleTransportInfo(*tinfo, trans_parsers, 654 &pair_elems, error)) 655 return false; 656 657 WriteJingleContentPair(content->name, pair_elems, elems); 658 } 659 return true; 660 } 661 662 bool ParseContentType(SignalingProtocol protocol, 663 const buzz::XmlElement* action_elem, 664 std::string* content_type, 665 ParseError* error) { 666 const buzz::XmlElement* content_elem; 667 if (protocol == PROTOCOL_GINGLE) { 668 if (!ParseContentType(action_elem, content_type, &content_elem, error)) 669 return false; 670 671 // Internally, we only use NS_JINGLE_RTP. 672 if (*content_type == NS_GINGLE_AUDIO || 673 *content_type == NS_GINGLE_VIDEO) 674 *content_type = NS_JINGLE_RTP; 675 } else { 676 const buzz::XmlElement* pair_elem 677 = action_elem->FirstNamed(QN_JINGLE_CONTENT); 678 if (pair_elem == NULL) 679 return BadParse("No contents found", error); 680 681 if (!ParseContentType(pair_elem, content_type, &content_elem, error)) 682 return false; 683 684 // If there is more than one content type, return an error. 685 for (; pair_elem != NULL; 686 pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) { 687 std::string content_type2; 688 if (!ParseContentType(pair_elem, &content_type2, &content_elem, error)) 689 return false; 690 691 if (content_type2 != *content_type) 692 return BadParse("More than one content type found", error); 693 } 694 } 695 696 return true; 697 } 698 699 bool ParseSessionInitiate(SignalingProtocol protocol, 700 const buzz::XmlElement* action_elem, 701 const ContentParserMap& content_parsers, 702 const TransportParserMap& trans_parsers, 703 SessionInitiate* init, 704 ParseError* error) { 705 init->owns_contents = true; 706 if (protocol == PROTOCOL_GINGLE) { 707 if (!ParseGingleContentInfos(action_elem, content_parsers, 708 &init->contents, error)) 709 return false; 710 711 if (!ParseGingleTransportInfos(action_elem, init->contents, trans_parsers, 712 &init->transports, error)) 713 return false; 714 } else { 715 if (!ParseJingleContentInfos(action_elem, content_parsers, 716 &init->contents, error)) 717 return false; 718 719 if (!ParseJingleTransportInfos(action_elem, init->contents, trans_parsers, 720 &init->transports, error)) 721 return false; 722 } 723 724 return true; 725 } 726 727 728 bool WriteSessionInitiate(SignalingProtocol protocol, 729 const ContentInfos& contents, 730 const TransportInfos& tinfos, 731 const ContentParserMap& content_parsers, 732 const TransportParserMap& transport_parsers, 733 XmlElements* elems, 734 WriteError* error) { 735 if (protocol == PROTOCOL_GINGLE) { 736 if (!WriteGingleContentInfos(contents, content_parsers, elems, error)) 737 return false; 738 739 if (!WriteGingleTransportInfos(tinfos, transport_parsers, 740 elems, error)) 741 return false; 742 } else { 743 if (!WriteJingleContentPairs(contents, content_parsers, 744 tinfos, transport_parsers, 745 elems, error)) 746 return false; 747 } 748 749 return true; 750 } 751 752 bool ParseSessionAccept(SignalingProtocol protocol, 753 const buzz::XmlElement* action_elem, 754 const ContentParserMap& content_parsers, 755 const TransportParserMap& transport_parsers, 756 SessionAccept* accept, 757 ParseError* error) { 758 return ParseSessionInitiate(protocol, action_elem, 759 content_parsers, transport_parsers, 760 accept, error); 761 } 762 763 bool WriteSessionAccept(SignalingProtocol protocol, 764 const ContentInfos& contents, 765 const TransportInfos& tinfos, 766 const ContentParserMap& content_parsers, 767 const TransportParserMap& transport_parsers, 768 XmlElements* elems, 769 WriteError* error) { 770 return WriteSessionInitiate(protocol, contents, tinfos, 771 content_parsers, transport_parsers, 772 elems, error); 773 } 774 775 bool ParseSessionTerminate(SignalingProtocol protocol, 776 const buzz::XmlElement* action_elem, 777 SessionTerminate* term, 778 ParseError* error) { 779 if (protocol == PROTOCOL_GINGLE) { 780 const buzz::XmlElement* reason_elem = action_elem->FirstElement(); 781 if (reason_elem != NULL) { 782 term->reason = reason_elem->Name().LocalPart(); 783 const buzz::XmlElement *debug_elem = reason_elem->FirstElement(); 784 if (debug_elem != NULL) { 785 term->debug_reason = debug_elem->Name().LocalPart(); 786 } 787 } 788 return true; 789 } else { 790 const buzz::XmlElement* reason_elem = 791 action_elem->FirstNamed(QN_JINGLE_REASON); 792 if (reason_elem) { 793 reason_elem = reason_elem->FirstElement(); 794 if (reason_elem) { 795 term->reason = reason_elem->Name().LocalPart(); 796 } 797 } 798 return true; 799 } 800 } 801 802 void WriteSessionTerminate(SignalingProtocol protocol, 803 const SessionTerminate& term, 804 XmlElements* elems) { 805 if (protocol == PROTOCOL_GINGLE) { 806 elems->push_back(new buzz::XmlElement( 807 buzz::QName(true, NS_GINGLE, term.reason))); 808 } else { 809 if (!term.reason.empty()) { 810 buzz::XmlElement* reason_elem = new buzz::XmlElement(QN_JINGLE_REASON); 811 reason_elem->AddElement(new buzz::XmlElement( 812 buzz::QName(true, NS_JINGLE, term.reason))); 813 elems->push_back(reason_elem); 814 } 815 } 816 } 817 818 bool ParseTransportInfos(SignalingProtocol protocol, 819 const buzz::XmlElement* action_elem, 820 const ContentInfos& contents, 821 const TransportParserMap& trans_parsers, 822 TransportInfos* tinfos, 823 ParseError* error) { 824 if (protocol == PROTOCOL_GINGLE) { 825 return ParseGingleTransportInfos( 826 action_elem, contents, trans_parsers, tinfos, error); 827 } else { 828 return ParseJingleTransportInfos( 829 action_elem, contents, trans_parsers, tinfos, error); 830 } 831 } 832 833 bool WriteTransportInfos(SignalingProtocol protocol, 834 const TransportInfos& tinfos, 835 const TransportParserMap& trans_parsers, 836 XmlElements* elems, 837 WriteError* error) { 838 if (protocol == PROTOCOL_GINGLE) { 839 return WriteGingleTransportInfos(tinfos, trans_parsers, 840 elems, error); 841 } else { 842 return WriteJingleTransportInfos(tinfos, trans_parsers, 843 elems, error); 844 } 845 } 846 847 bool ParseSessionNotify(const buzz::XmlElement* action_elem, 848 SessionNotify* notify, ParseError* error) { 849 const buzz::XmlElement* notify_elem; 850 for (notify_elem = action_elem->FirstNamed(QN_GINGLE_NOTIFY); 851 notify_elem != NULL; 852 notify_elem = notify_elem->NextNamed(QN_GINGLE_NOTIFY)) { 853 // Note that a subsequent notify element for the same user will override a 854 // previous. We don't merge them. 855 std::string nick(notify_elem->Attr(QN_GINGLE_NOTIFY_NICK)); 856 if (nick != buzz::STR_EMPTY) { 857 MediaSources sources; 858 const buzz::XmlElement* source_elem; 859 for (source_elem = notify_elem->FirstNamed(QN_GINGLE_NOTIFY_SOURCE); 860 source_elem != NULL; 861 source_elem = source_elem->NextNamed(QN_GINGLE_NOTIFY_SOURCE)) { 862 std::string ssrc = source_elem->Attr(QN_GINGLE_NOTIFY_SOURCE_SSRC); 863 if (ssrc != buzz::STR_EMPTY) { 864 std::string mtype = source_elem->Attr(QN_GINGLE_NOTIFY_SOURCE_MTYPE); 865 if (mtype == GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO) { 866 sources.audio_ssrc = strtoul(ssrc.c_str(), NULL, 10); 867 } else if (mtype == GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO) { 868 sources.video_ssrc = strtoul(ssrc.c_str(), NULL, 10); 869 } 870 } 871 } 872 873 notify->nickname_to_sources.insert( 874 std::pair<std::string, MediaSources>(nick, sources)); 875 } 876 } 877 878 return true; 879 } 880 881 bool GetUriTarget(const std::string& prefix, const std::string& str, 882 std::string* after) { 883 size_t pos = str.find(prefix); 884 if (pos == std::string::npos) 885 return false; 886 887 *after = str.substr(pos + prefix.size(), std::string::npos); 888 return true; 889 } 890 891 bool ParseSessionUpdate(const buzz::XmlElement* action_elem, 892 SessionUpdate* update, ParseError* error) { 893 // TODO: Parse the update message. 894 return true; 895 } 896 897 void WriteSessionView(const SessionView& view, XmlElements* elems) { 898 std::vector<VideoViewRequest>::const_iterator it; 899 for (it = view.view_requests.begin(); it != view.view_requests.end(); it++) { 900 talk_base::scoped_ptr<buzz::XmlElement> view_elem( 901 new buzz::XmlElement(QN_GINGLE_VIEW)); 902 if (view_elem.get() == NULL) { 903 return; 904 } 905 906 view_elem->SetAttr(QN_GINGLE_VIEW_TYPE, GINGLE_VIEW_TYPE_STATIC); 907 view_elem->SetAttr(QN_GINGLE_VIEW_NICK, it->nick_name); 908 view_elem->SetAttr(QN_GINGLE_VIEW_MEDIA_TYPE, 909 GINGLE_VIEW_MEDIA_TYPE_VIDEO); 910 911 // A 32-bit uint, expressed as decimal, has a max of 10 digits, plus one 912 // for the null. 913 char str[11]; 914 int result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->ssrc); 915 if (result < 0 || result >= ARRAY_SIZE(str)) { 916 continue; 917 } 918 view_elem->SetAttr(QN_GINGLE_VIEW_SSRC, str); 919 920 // Include video-specific parameters in a child <params> element. 921 talk_base::scoped_ptr<buzz::XmlElement> params_elem( 922 new buzz::XmlElement(QN_GINGLE_VIEW_PARAMS)); 923 if (params_elem.get() == NULL) { 924 return; 925 } 926 927 result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->width); 928 if (result < 0 || result >= ARRAY_SIZE(str)) { 929 continue; 930 } 931 params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_WIDTH, str); 932 933 result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->height); 934 if (result < 0 || result >= ARRAY_SIZE(str)) { 935 continue; 936 } 937 params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_HEIGHT, str); 938 939 result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->framerate); 940 if (result < 0 || result >= ARRAY_SIZE(str)) { 941 continue; 942 } 943 params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_FRAMERATE, str); 944 945 view_elem->AddElement(params_elem.release()); 946 elems->push_back(view_elem.release()); 947 } 948 } 949 950 bool FindSessionRedirect(const buzz::XmlElement* stanza, 951 SessionRedirect* redirect) { 952 const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR); 953 if (error_elem == NULL) 954 return false; 955 956 const buzz::XmlElement* redirect_elem = 957 error_elem->FirstNamed(QN_GINGLE_REDIRECT); 958 if (redirect_elem == NULL) 959 redirect_elem = error_elem->FirstNamed(buzz::QN_STANZA_REDIRECT); 960 if (redirect_elem == NULL) 961 return false; 962 963 if (!GetUriTarget(STR_REDIRECT_PREFIX, redirect_elem->BodyText(), 964 &redirect->target)) 965 return false; 966 967 return true; 968 } 969 970 } // namespace cricket 971