3 * Copyright 2010, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include "talk/p2p/base/sessionmessages.h"
33 #include "talk/p2p/base/constants.h"
34 #include "talk/p2p/base/p2ptransport.h"
35 #include "talk/p2p/base/parsing.h"
36 #include "talk/p2p/base/sessionclient.h"
37 #include "talk/p2p/base/sessiondescription.h"
38 #include "talk/p2p/base/transport.h"
39 #include "webrtc/libjingle/xmllite/xmlconstants.h"
40 #include "talk/xmpp/constants.h"
41 #include "webrtc/base/logging.h"
42 #include "webrtc/base/scoped_ptr.h"
43 #include "webrtc/base/stringutils.h"
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 == JINGLE_ACTION_DESCRIPTION_INFO)
77 return ACTION_DESCRIPTION_INFO;
78 if (type == GINGLE_ACTION_UPDATE)
79 return ACTION_DESCRIPTION_INFO;
81 return ACTION_UNKNOWN;
84 std::string ToJingleString(ActionType 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_DESCRIPTION_INFO:
91 return JINGLE_ACTION_DESCRIPTION_INFO;
92 case ACTION_SESSION_ACCEPT:
93 return JINGLE_ACTION_SESSION_ACCEPT;
94 // Notice that reject and terminate both go to
95 // "session-terminate", but there is no "session-reject".
96 case ACTION_SESSION_REJECT:
97 case ACTION_SESSION_TERMINATE:
98 return JINGLE_ACTION_SESSION_TERMINATE;
99 case ACTION_TRANSPORT_INFO:
100 return JINGLE_ACTION_TRANSPORT_INFO;
101 case ACTION_TRANSPORT_ACCEPT:
102 return JINGLE_ACTION_TRANSPORT_ACCEPT;
108 std::string ToGingleString(ActionType type) {
110 case ACTION_SESSION_INITIATE:
111 return GINGLE_ACTION_INITIATE;
112 case ACTION_SESSION_INFO:
113 return GINGLE_ACTION_INFO;
114 case ACTION_SESSION_ACCEPT:
115 return GINGLE_ACTION_ACCEPT;
116 case ACTION_SESSION_REJECT:
117 return GINGLE_ACTION_REJECT;
118 case ACTION_SESSION_TERMINATE:
119 return GINGLE_ACTION_TERMINATE;
120 case ACTION_TRANSPORT_INFO:
121 return GINGLE_ACTION_CANDIDATES;
128 bool IsJingleMessage(const buzz::XmlElement* stanza) {
129 const buzz::XmlElement* jingle = stanza->FirstNamed(QN_JINGLE);
133 return (jingle->HasAttr(buzz::QN_ACTION) && jingle->HasAttr(QN_SID));
136 bool IsGingleMessage(const buzz::XmlElement* stanza) {
137 const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION);
141 return (session->HasAttr(buzz::QN_TYPE) &&
142 session->HasAttr(buzz::QN_ID) &&
143 session->HasAttr(QN_INITIATOR));
146 bool IsSessionMessage(const buzz::XmlElement* stanza) {
147 return (stanza->Name() == buzz::QN_IQ &&
148 stanza->Attr(buzz::QN_TYPE) == buzz::STR_SET &&
149 (IsJingleMessage(stanza) ||
150 IsGingleMessage(stanza)));
153 bool ParseGingleSessionMessage(const buzz::XmlElement* session,
156 msg->protocol = PROTOCOL_GINGLE;
157 std::string type_string = session->Attr(buzz::QN_TYPE);
158 msg->type = ToActionType(type_string);
159 msg->sid = session->Attr(buzz::QN_ID);
160 msg->initiator = session->Attr(QN_INITIATOR);
161 msg->action_elem = session;
163 if (msg->type == ACTION_UNKNOWN)
164 return BadParse("unknown action: " + type_string, error);
169 bool ParseJingleSessionMessage(const buzz::XmlElement* jingle,
172 msg->protocol = PROTOCOL_JINGLE;
173 std::string type_string = jingle->Attr(buzz::QN_ACTION);
174 msg->type = ToActionType(type_string);
175 msg->sid = jingle->Attr(QN_SID);
176 msg->initiator = GetXmlAttr(jingle, QN_INITIATOR, buzz::STR_EMPTY);
177 msg->action_elem = jingle;
179 if (msg->type == ACTION_UNKNOWN)
180 return BadParse("unknown action: " + type_string, error);
185 bool ParseHybridSessionMessage(const buzz::XmlElement* jingle,
188 if (!ParseJingleSessionMessage(jingle, msg, error))
190 msg->protocol = PROTOCOL_HYBRID;
195 bool ParseSessionMessage(const buzz::XmlElement* stanza,
198 msg->id = stanza->Attr(buzz::QN_ID);
199 msg->from = stanza->Attr(buzz::QN_FROM);
200 msg->to = stanza->Attr(buzz::QN_TO);
201 msg->stanza = stanza;
203 const buzz::XmlElement* jingle = stanza->FirstNamed(QN_JINGLE);
204 const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION);
205 if (jingle && session)
206 return ParseHybridSessionMessage(jingle, msg, error);
208 return ParseJingleSessionMessage(jingle, msg, error);
210 return ParseGingleSessionMessage(session, msg, error);
214 buzz::XmlElement* WriteGingleAction(const SessionMessage& msg,
215 const XmlElements& action_elems) {
216 buzz::XmlElement* session = new buzz::XmlElement(QN_GINGLE_SESSION, true);
217 session->AddAttr(buzz::QN_TYPE, ToGingleString(msg.type));
218 session->AddAttr(buzz::QN_ID, msg.sid);
219 session->AddAttr(QN_INITIATOR, msg.initiator);
220 AddXmlChildren(session, action_elems);
224 buzz::XmlElement* WriteJingleAction(const SessionMessage& msg,
225 const XmlElements& action_elems) {
226 buzz::XmlElement* jingle = new buzz::XmlElement(QN_JINGLE, true);
227 jingle->AddAttr(buzz::QN_ACTION, ToJingleString(msg.type));
228 jingle->AddAttr(QN_SID, msg.sid);
229 if (msg.type == ACTION_SESSION_INITIATE) {
230 jingle->AddAttr(QN_INITIATOR, msg.initiator);
232 AddXmlChildren(jingle, action_elems);
236 void WriteSessionMessage(const SessionMessage& msg,
237 const XmlElements& action_elems,
238 buzz::XmlElement* stanza) {
239 stanza->SetAttr(buzz::QN_TO, msg.to);
240 stanza->SetAttr(buzz::QN_TYPE, buzz::STR_SET);
242 if (msg.protocol == PROTOCOL_GINGLE) {
243 stanza->AddElement(WriteGingleAction(msg, action_elems));
245 stanza->AddElement(WriteJingleAction(msg, action_elems));
250 TransportParser* GetTransportParser(const TransportParserMap& trans_parsers,
251 const std::string& transport_type) {
252 TransportParserMap::const_iterator map = trans_parsers.find(transport_type);
253 if (map == trans_parsers.end()) {
260 CandidateTranslator* GetCandidateTranslator(
261 const CandidateTranslatorMap& translators,
262 const std::string& content_name) {
263 CandidateTranslatorMap::const_iterator map = translators.find(content_name);
264 if (map == translators.end()) {
271 bool GetParserAndTranslator(const TransportParserMap& trans_parsers,
272 const CandidateTranslatorMap& translators,
273 const std::string& transport_type,
274 const std::string& content_name,
275 TransportParser** parser,
276 CandidateTranslator** translator,
278 *parser = GetTransportParser(trans_parsers, transport_type);
279 if (*parser == NULL) {
280 return BadParse("unknown transport type: " + transport_type, error);
282 // Not having a translator isn't fatal when parsing. If this is called for an
283 // initiate message, we won't have our proxies set up to do the translation.
284 // Fortunately, for the cases where translation is needed, candidates are
285 // never sent in initiates.
286 *translator = GetCandidateTranslator(translators, content_name);
290 bool GetParserAndTranslator(const TransportParserMap& trans_parsers,
291 const CandidateTranslatorMap& translators,
292 const std::string& transport_type,
293 const std::string& content_name,
294 TransportParser** parser,
295 CandidateTranslator** translator,
297 *parser = GetTransportParser(trans_parsers, transport_type);
298 if (*parser == NULL) {
299 return BadWrite("unknown transport type: " + transport_type, error);
301 *translator = GetCandidateTranslator(translators, content_name);
302 if (*translator == NULL) {
303 return BadWrite("unknown content name: " + content_name, error);
308 bool ParseGingleCandidate(const buzz::XmlElement* candidate_elem,
309 const TransportParserMap& trans_parsers,
310 const CandidateTranslatorMap& translators,
311 const std::string& content_name,
312 Candidates* candidates,
314 TransportParser* trans_parser;
315 CandidateTranslator* translator;
316 if (!GetParserAndTranslator(trans_parsers, translators,
317 NS_GINGLE_P2P, content_name,
318 &trans_parser, &translator, error))
322 if (!trans_parser->ParseGingleCandidate(
323 candidate_elem, translator, &candidate, error)) {
327 candidates->push_back(candidate);
331 bool ParseGingleCandidates(const buzz::XmlElement* parent,
332 const TransportParserMap& trans_parsers,
333 const CandidateTranslatorMap& translators,
334 const std::string& content_name,
335 Candidates* candidates,
337 for (const buzz::XmlElement* candidate_elem = parent->FirstElement();
338 candidate_elem != NULL;
339 candidate_elem = candidate_elem->NextElement()) {
340 if (candidate_elem->Name().LocalPart() == LN_CANDIDATE) {
341 if (!ParseGingleCandidate(candidate_elem, trans_parsers, translators,
342 content_name, candidates, error)) {
350 bool ParseGingleTransportInfos(const buzz::XmlElement* action_elem,
351 const ContentInfos& contents,
352 const TransportParserMap& trans_parsers,
353 const CandidateTranslatorMap& translators,
354 TransportInfos* tinfos,
356 bool has_audio = FindContentInfoByName(contents, CN_AUDIO) != NULL;
357 bool has_video = FindContentInfoByName(contents, CN_VIDEO) != NULL;
359 // If we don't have media, no need to separate the candidates.
360 if (!has_audio && !has_video) {
361 TransportInfo tinfo(CN_OTHER,
362 TransportDescription(NS_GINGLE_P2P, std::string(), std::string()));
363 if (!ParseGingleCandidates(action_elem, trans_parsers, translators,
364 CN_OTHER, &tinfo.description.candidates,
369 tinfos->push_back(tinfo);
373 // If we have media, separate the candidates.
374 TransportInfo audio_tinfo(
376 TransportDescription(NS_GINGLE_P2P, std::string(), std::string()));
377 TransportInfo video_tinfo(
379 TransportDescription(NS_GINGLE_P2P, std::string(), std::string()));
380 for (const buzz::XmlElement* candidate_elem = action_elem->FirstElement();
381 candidate_elem != NULL;
382 candidate_elem = candidate_elem->NextElement()) {
383 if (candidate_elem->Name().LocalPart() == LN_CANDIDATE) {
384 const std::string& channel_name = candidate_elem->Attr(buzz::QN_NAME);
386 (channel_name == GICE_CHANNEL_NAME_RTP ||
387 channel_name == GICE_CHANNEL_NAME_RTCP)) {
388 if (!ParseGingleCandidate(
389 candidate_elem, trans_parsers,
390 translators, CN_AUDIO,
391 &audio_tinfo.description.candidates, error)) {
394 } else if (has_video &&
395 (channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
396 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP)) {
397 if (!ParseGingleCandidate(
398 candidate_elem, trans_parsers,
399 translators, CN_VIDEO,
400 &video_tinfo.description.candidates, error)) {
404 return BadParse("Unknown channel name: " + channel_name, error);
410 tinfos->push_back(audio_tinfo);
413 tinfos->push_back(video_tinfo);
418 bool ParseJingleTransportInfo(const buzz::XmlElement* trans_elem,
419 const std::string& content_name,
420 const TransportParserMap& trans_parsers,
421 const CandidateTranslatorMap& translators,
422 TransportInfo* tinfo,
424 TransportParser* trans_parser;
425 CandidateTranslator* translator;
426 if (!GetParserAndTranslator(trans_parsers, translators,
427 trans_elem->Name().Namespace(), content_name,
428 &trans_parser, &translator, error))
431 TransportDescription tdesc;
432 if (!trans_parser->ParseTransportDescription(trans_elem, translator,
436 *tinfo = TransportInfo(content_name, tdesc);
440 bool ParseJingleTransportInfos(const buzz::XmlElement* jingle,
441 const ContentInfos& contents,
442 const TransportParserMap trans_parsers,
443 const CandidateTranslatorMap& translators,
444 TransportInfos* tinfos,
446 for (const buzz::XmlElement* pair_elem
447 = jingle->FirstNamed(QN_JINGLE_CONTENT);
449 pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) {
450 std::string content_name;
451 if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME,
452 &content_name, error))
455 const ContentInfo* content = FindContentInfoByName(contents, content_name);
457 return BadParse("Unknown content name: " + content_name, error);
459 const buzz::XmlElement* trans_elem;
460 if (!RequireXmlChild(pair_elem, LN_TRANSPORT, &trans_elem, error))
464 if (!ParseJingleTransportInfo(trans_elem, content->name,
465 trans_parsers, translators,
469 tinfos->push_back(tinfo);
475 buzz::XmlElement* NewTransportElement(const std::string& name) {
476 return new buzz::XmlElement(buzz::QName(name, LN_TRANSPORT), true);
479 bool WriteGingleCandidates(const Candidates& candidates,
480 const TransportParserMap& trans_parsers,
481 const std::string& transport_type,
482 const CandidateTranslatorMap& translators,
483 const std::string& content_name,
486 TransportParser* trans_parser;
487 CandidateTranslator* translator;
488 if (!GetParserAndTranslator(trans_parsers, translators,
489 transport_type, content_name,
490 &trans_parser, &translator, error))
493 for (size_t i = 0; i < candidates.size(); ++i) {
494 rtc::scoped_ptr<buzz::XmlElement> element;
495 if (!trans_parser->WriteGingleCandidate(candidates[i], translator,
496 element.accept(), error)) {
500 elems->push_back(element.release());
506 bool WriteGingleTransportInfos(const TransportInfos& tinfos,
507 const TransportParserMap& trans_parsers,
508 const CandidateTranslatorMap& translators,
511 for (TransportInfos::const_iterator tinfo = tinfos.begin();
512 tinfo != tinfos.end(); ++tinfo) {
513 if (!WriteGingleCandidates(tinfo->description.candidates,
514 trans_parsers, tinfo->description.transport_type,
515 translators, tinfo->content_name,
523 bool WriteJingleTransportInfo(const TransportInfo& tinfo,
524 const TransportParserMap& trans_parsers,
525 const CandidateTranslatorMap& translators,
528 std::string transport_type = tinfo.description.transport_type;
529 TransportParser* trans_parser;
530 CandidateTranslator* translator;
531 if (!GetParserAndTranslator(trans_parsers, translators,
532 transport_type, tinfo.content_name,
533 &trans_parser, &translator, error))
536 buzz::XmlElement* trans_elem;
537 if (!trans_parser->WriteTransportDescription(tinfo.description, translator,
538 &trans_elem, error)) {
542 elems->push_back(trans_elem);
546 void WriteJingleContent(const std::string name,
547 const XmlElements& child_elems,
548 XmlElements* elems) {
549 buzz::XmlElement* content_elem = new buzz::XmlElement(QN_JINGLE_CONTENT);
550 content_elem->SetAttr(QN_JINGLE_CONTENT_NAME, name);
551 content_elem->SetAttr(QN_CREATOR, LN_INITIATOR);
552 AddXmlChildren(content_elem, child_elems);
554 elems->push_back(content_elem);
557 bool WriteJingleTransportInfos(const TransportInfos& tinfos,
558 const TransportParserMap& trans_parsers,
559 const CandidateTranslatorMap& translators,
562 for (TransportInfos::const_iterator tinfo = tinfos.begin();
563 tinfo != tinfos.end(); ++tinfo) {
564 XmlElements content_child_elems;
565 if (!WriteJingleTransportInfo(*tinfo, trans_parsers, translators,
566 &content_child_elems, error))
570 WriteJingleContent(tinfo->content_name, content_child_elems, elems);
576 ContentParser* GetContentParser(const ContentParserMap& content_parsers,
577 const std::string& type) {
578 ContentParserMap::const_iterator map = content_parsers.find(type);
579 if (map == content_parsers.end()) {
586 bool ParseContentInfo(SignalingProtocol protocol,
587 const std::string& name,
588 const std::string& type,
589 const buzz::XmlElement* elem,
590 const ContentParserMap& parsers,
591 ContentInfos* contents,
593 ContentParser* parser = GetContentParser(parsers, type);
595 return BadParse("unknown application content: " + type, error);
597 ContentDescription* desc;
598 if (!parser->ParseContent(protocol, elem, &desc, error))
601 contents->push_back(ContentInfo(name, type, desc));
605 bool ParseContentType(const buzz::XmlElement* parent_elem,
606 std::string* content_type,
607 const buzz::XmlElement** content_elem,
609 if (!RequireXmlChild(parent_elem, LN_DESCRIPTION, content_elem, error))
612 *content_type = (*content_elem)->Name().Namespace();
616 bool ParseGingleContentInfos(const buzz::XmlElement* session,
617 const ContentParserMap& content_parsers,
618 ContentInfos* contents,
620 std::string content_type;
621 const buzz::XmlElement* content_elem;
622 if (!ParseContentType(session, &content_type, &content_elem, error))
625 if (content_type == NS_GINGLE_VIDEO) {
626 // A parser parsing audio or video content should look at the
627 // namespace and only parse the codecs relevant to that namespace.
628 // We use this to control which codecs get parsed: first audio,
630 rtc::scoped_ptr<buzz::XmlElement> audio_elem(
631 new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT));
632 CopyXmlChildren(content_elem, audio_elem.get());
633 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP,
634 audio_elem.get(), content_parsers,
638 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_VIDEO, NS_JINGLE_RTP,
639 content_elem, content_parsers,
642 } else if (content_type == NS_GINGLE_AUDIO) {
643 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP,
644 content_elem, content_parsers,
648 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_OTHER, content_type,
649 content_elem, content_parsers,
656 bool ParseJingleContentInfos(const buzz::XmlElement* jingle,
657 const ContentParserMap& content_parsers,
658 ContentInfos* contents,
660 for (const buzz::XmlElement* pair_elem
661 = jingle->FirstNamed(QN_JINGLE_CONTENT);
663 pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) {
664 std::string content_name;
665 if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME,
666 &content_name, error))
669 std::string content_type;
670 const buzz::XmlElement* content_elem;
671 if (!ParseContentType(pair_elem, &content_type, &content_elem, error))
674 if (!ParseContentInfo(PROTOCOL_JINGLE, content_name, content_type,
675 content_elem, content_parsers,
682 bool ParseJingleGroupInfos(const buzz::XmlElement* jingle,
683 ContentGroups* groups,
685 for (const buzz::XmlElement* pair_elem
686 = jingle->FirstNamed(QN_JINGLE_DRAFT_GROUP);
688 pair_elem = pair_elem->NextNamed(QN_JINGLE_DRAFT_GROUP)) {
689 std::string group_name;
690 if (!RequireXmlAttr(pair_elem, QN_JINGLE_DRAFT_GROUP_TYPE,
694 ContentGroup group(group_name);
695 for (const buzz::XmlElement* child_elem
696 = pair_elem->FirstNamed(QN_JINGLE_CONTENT);
698 child_elem = child_elem->NextNamed(QN_JINGLE_CONTENT)) {
699 std::string content_name;
700 if (!RequireXmlAttr(child_elem, QN_JINGLE_CONTENT_NAME,
701 &content_name, error))
703 group.AddContentName(content_name);
705 groups->push_back(group);
710 buzz::XmlElement* WriteContentInfo(SignalingProtocol protocol,
711 const ContentInfo& content,
712 const ContentParserMap& parsers,
714 ContentParser* parser = GetContentParser(parsers, content.type);
715 if (parser == NULL) {
716 BadWrite("unknown content type: " + content.type, error);
720 buzz::XmlElement* elem = NULL;
721 if (!parser->WriteContent(protocol, content.description, &elem, error))
727 bool IsWritable(SignalingProtocol protocol,
728 const ContentInfo& content,
729 const ContentParserMap& parsers) {
730 ContentParser* parser = GetContentParser(parsers, content.type);
731 if (parser == NULL) {
735 return parser->IsWritable(protocol, content.description);
738 bool WriteGingleContentInfos(const ContentInfos& contents,
739 const ContentParserMap& parsers,
742 if (contents.size() == 1 ||
743 (contents.size() == 2 &&
744 !IsWritable(PROTOCOL_GINGLE, contents.at(1), parsers))) {
745 if (contents.front().rejected) {
746 return BadWrite("Gingle protocol may not reject individual contents.",
749 buzz::XmlElement* elem = WriteContentInfo(
750 PROTOCOL_GINGLE, contents.front(), parsers, error);
754 elems->push_back(elem);
755 } else if (contents.size() >= 2 &&
756 contents.at(0).type == NS_JINGLE_RTP &&
757 contents.at(1).type == NS_JINGLE_RTP) {
758 // Special-case audio + video contents so that they are "merged"
759 // into one "video" content.
760 if (contents.at(0).rejected || contents.at(1).rejected) {
761 return BadWrite("Gingle protocol may not reject individual contents.",
764 buzz::XmlElement* audio = WriteContentInfo(
765 PROTOCOL_GINGLE, contents.at(0), parsers, error);
769 buzz::XmlElement* video = WriteContentInfo(
770 PROTOCOL_GINGLE, contents.at(1), parsers, error);
776 CopyXmlChildren(audio, video);
777 elems->push_back(video);
780 return BadWrite("Gingle protocol may only have one content.", error);
786 const TransportInfo* GetTransportInfoByContentName(
787 const TransportInfos& tinfos, const std::string& content_name) {
788 for (TransportInfos::const_iterator tinfo = tinfos.begin();
789 tinfo != tinfos.end(); ++tinfo) {
790 if (content_name == tinfo->content_name) {
797 bool WriteJingleContents(const ContentInfos& contents,
798 const ContentParserMap& content_parsers,
799 const TransportInfos& tinfos,
800 const TransportParserMap& trans_parsers,
801 const CandidateTranslatorMap& translators,
804 for (ContentInfos::const_iterator content = contents.begin();
805 content != contents.end(); ++content) {
806 if (content->rejected) {
809 const TransportInfo* tinfo =
810 GetTransportInfoByContentName(tinfos, content->name);
812 return BadWrite("No transport for content: " + content->name, error);
814 XmlElements pair_elems;
815 buzz::XmlElement* elem = WriteContentInfo(
816 PROTOCOL_JINGLE, *content, content_parsers, error);
819 pair_elems.push_back(elem);
821 if (!WriteJingleTransportInfo(*tinfo, trans_parsers, translators,
825 WriteJingleContent(content->name, pair_elems, elems);
830 bool WriteJingleContentInfos(const ContentInfos& contents,
831 const ContentParserMap& content_parsers,
834 for (ContentInfos::const_iterator content = contents.begin();
835 content != contents.end(); ++content) {
836 if (content->rejected) {
839 XmlElements content_child_elems;
840 buzz::XmlElement* elem = WriteContentInfo(
841 PROTOCOL_JINGLE, *content, content_parsers, error);
844 content_child_elems.push_back(elem);
845 WriteJingleContent(content->name, content_child_elems, elems);
850 bool WriteJingleGroupInfo(const ContentInfos& contents,
851 const ContentGroups& groups,
854 if (!groups.empty()) {
855 buzz::XmlElement* pair_elem = new buzz::XmlElement(QN_JINGLE_DRAFT_GROUP);
856 pair_elem->SetAttr(QN_JINGLE_DRAFT_GROUP_TYPE, GROUP_TYPE_BUNDLE);
858 XmlElements pair_elems;
859 for (ContentInfos::const_iterator content = contents.begin();
860 content != contents.end(); ++content) {
861 buzz::XmlElement* child_elem =
862 new buzz::XmlElement(QN_JINGLE_CONTENT, false);
863 child_elem->SetAttr(QN_JINGLE_CONTENT_NAME, content->name);
864 pair_elems.push_back(child_elem);
866 AddXmlChildren(pair_elem, pair_elems);
867 elems->push_back(pair_elem);
872 bool ParseContentType(SignalingProtocol protocol,
873 const buzz::XmlElement* action_elem,
874 std::string* content_type,
876 const buzz::XmlElement* content_elem;
877 if (protocol == PROTOCOL_GINGLE) {
878 if (!ParseContentType(action_elem, content_type, &content_elem, error))
881 // Internally, we only use NS_JINGLE_RTP.
882 if (*content_type == NS_GINGLE_AUDIO ||
883 *content_type == NS_GINGLE_VIDEO)
884 *content_type = NS_JINGLE_RTP;
886 const buzz::XmlElement* pair_elem
887 = action_elem->FirstNamed(QN_JINGLE_CONTENT);
888 if (pair_elem == NULL)
889 return BadParse("No contents found", error);
891 if (!ParseContentType(pair_elem, content_type, &content_elem, error))
898 static bool ParseContentMessage(
899 SignalingProtocol protocol,
900 const buzz::XmlElement* action_elem,
901 bool expect_transports,
902 const ContentParserMap& content_parsers,
903 const TransportParserMap& trans_parsers,
904 const CandidateTranslatorMap& translators,
905 SessionInitiate* init,
907 init->owns_contents = true;
908 if (protocol == PROTOCOL_GINGLE) {
909 if (!ParseGingleContentInfos(action_elem, content_parsers,
910 &init->contents, error))
913 if (expect_transports &&
914 !ParseGingleTransportInfos(action_elem, init->contents,
915 trans_parsers, translators,
916 &init->transports, error))
919 if (!ParseJingleContentInfos(action_elem, content_parsers,
920 &init->contents, error))
922 if (!ParseJingleGroupInfos(action_elem, &init->groups, error))
925 if (expect_transports &&
926 !ParseJingleTransportInfos(action_elem, init->contents,
927 trans_parsers, translators,
928 &init->transports, error))
935 static bool WriteContentMessage(
936 SignalingProtocol protocol,
937 const ContentInfos& contents,
938 const TransportInfos& tinfos,
939 const ContentParserMap& content_parsers,
940 const TransportParserMap& transport_parsers,
941 const CandidateTranslatorMap& translators,
942 const ContentGroups& groups,
945 if (protocol == PROTOCOL_GINGLE) {
946 if (!WriteGingleContentInfos(contents, content_parsers, elems, error))
949 if (!WriteGingleTransportInfos(tinfos, transport_parsers, translators,
953 if (!WriteJingleContents(contents, content_parsers,
954 tinfos, transport_parsers, translators,
957 if (!WriteJingleGroupInfo(contents, groups, elems, error))
964 bool ParseSessionInitiate(SignalingProtocol protocol,
965 const buzz::XmlElement* action_elem,
966 const ContentParserMap& content_parsers,
967 const TransportParserMap& trans_parsers,
968 const CandidateTranslatorMap& translators,
969 SessionInitiate* init,
971 bool expect_transports = true;
972 return ParseContentMessage(protocol, action_elem, expect_transports,
973 content_parsers, trans_parsers, translators,
978 bool WriteSessionInitiate(SignalingProtocol protocol,
979 const ContentInfos& contents,
980 const TransportInfos& tinfos,
981 const ContentParserMap& content_parsers,
982 const TransportParserMap& transport_parsers,
983 const CandidateTranslatorMap& translators,
984 const ContentGroups& groups,
987 return WriteContentMessage(protocol, contents, tinfos,
988 content_parsers, transport_parsers, translators,
993 bool ParseSessionAccept(SignalingProtocol protocol,
994 const buzz::XmlElement* action_elem,
995 const ContentParserMap& content_parsers,
996 const TransportParserMap& transport_parsers,
997 const CandidateTranslatorMap& translators,
998 SessionAccept* accept,
1000 bool expect_transports = true;
1001 return ParseContentMessage(protocol, action_elem, expect_transports,
1002 content_parsers, transport_parsers, translators,
1006 bool WriteSessionAccept(SignalingProtocol protocol,
1007 const ContentInfos& contents,
1008 const TransportInfos& tinfos,
1009 const ContentParserMap& content_parsers,
1010 const TransportParserMap& transport_parsers,
1011 const CandidateTranslatorMap& translators,
1012 const ContentGroups& groups,
1014 WriteError* error) {
1015 return WriteContentMessage(protocol, contents, tinfos,
1016 content_parsers, transport_parsers, translators,
1021 bool ParseSessionTerminate(SignalingProtocol protocol,
1022 const buzz::XmlElement* action_elem,
1023 SessionTerminate* term,
1024 ParseError* error) {
1025 if (protocol == PROTOCOL_GINGLE) {
1026 const buzz::XmlElement* reason_elem = action_elem->FirstElement();
1027 if (reason_elem != NULL) {
1028 term->reason = reason_elem->Name().LocalPart();
1029 const buzz::XmlElement *debug_elem = reason_elem->FirstElement();
1030 if (debug_elem != NULL) {
1031 term->debug_reason = debug_elem->Name().LocalPart();
1036 const buzz::XmlElement* reason_elem =
1037 action_elem->FirstNamed(QN_JINGLE_REASON);
1039 reason_elem = reason_elem->FirstElement();
1041 term->reason = reason_elem->Name().LocalPart();
1048 void WriteSessionTerminate(SignalingProtocol protocol,
1049 const SessionTerminate& term,
1050 XmlElements* elems) {
1051 if (protocol == PROTOCOL_GINGLE) {
1052 elems->push_back(new buzz::XmlElement(buzz::QName(NS_GINGLE, term.reason)));
1054 if (!term.reason.empty()) {
1055 buzz::XmlElement* reason_elem = new buzz::XmlElement(QN_JINGLE_REASON);
1056 reason_elem->AddElement(new buzz::XmlElement(
1057 buzz::QName(NS_JINGLE, term.reason)));
1058 elems->push_back(reason_elem);
1063 bool ParseDescriptionInfo(SignalingProtocol protocol,
1064 const buzz::XmlElement* action_elem,
1065 const ContentParserMap& content_parsers,
1066 const TransportParserMap& transport_parsers,
1067 const CandidateTranslatorMap& translators,
1068 DescriptionInfo* description_info,
1069 ParseError* error) {
1070 bool expect_transports = false;
1071 return ParseContentMessage(protocol, action_elem, expect_transports,
1072 content_parsers, transport_parsers, translators,
1073 description_info, error);
1076 bool WriteDescriptionInfo(SignalingProtocol protocol,
1077 const ContentInfos& contents,
1078 const ContentParserMap& content_parsers,
1080 WriteError* error) {
1081 if (protocol == PROTOCOL_GINGLE) {
1082 return WriteGingleContentInfos(contents, content_parsers, elems, error);
1084 return WriteJingleContentInfos(contents, content_parsers, elems, error);
1088 bool ParseTransportInfos(SignalingProtocol protocol,
1089 const buzz::XmlElement* action_elem,
1090 const ContentInfos& contents,
1091 const TransportParserMap& trans_parsers,
1092 const CandidateTranslatorMap& translators,
1093 TransportInfos* tinfos,
1094 ParseError* error) {
1095 if (protocol == PROTOCOL_GINGLE) {
1096 return ParseGingleTransportInfos(
1097 action_elem, contents, trans_parsers, translators, tinfos, error);
1099 return ParseJingleTransportInfos(
1100 action_elem, contents, trans_parsers, translators, tinfos, error);
1104 bool WriteTransportInfos(SignalingProtocol protocol,
1105 const TransportInfos& tinfos,
1106 const TransportParserMap& trans_parsers,
1107 const CandidateTranslatorMap& translators,
1109 WriteError* error) {
1110 if (protocol == PROTOCOL_GINGLE) {
1111 return WriteGingleTransportInfos(tinfos, trans_parsers, translators,
1114 return WriteJingleTransportInfos(tinfos, trans_parsers, translators,
1119 bool GetUriTarget(const std::string& prefix, const std::string& str,
1120 std::string* after) {
1121 size_t pos = str.find(prefix);
1122 if (pos == std::string::npos)
1125 *after = str.substr(pos + prefix.size(), std::string::npos);
1129 bool FindSessionRedirect(const buzz::XmlElement* stanza,
1130 SessionRedirect* redirect) {
1131 const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR);
1132 if (error_elem == NULL)
1135 const buzz::XmlElement* redirect_elem =
1136 error_elem->FirstNamed(QN_GINGLE_REDIRECT);
1137 if (redirect_elem == NULL)
1138 redirect_elem = error_elem->FirstNamed(buzz::QN_STANZA_REDIRECT);
1139 if (redirect_elem == NULL)
1142 if (!GetUriTarget(STR_REDIRECT_PREFIX, redirect_elem->BodyText(),
1149 } // namespace cricket