blob: 031c3d6f6aa78b07bb0f561d3b0b079077791e0e [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
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 "talk/p2p/base/sessionmessages.h"
29
30#include <stdio.h>
31#include <string>
32
33#include "talk/base/logging.h"
34#include "talk/base/scoped_ptr.h"
35#include "talk/base/stringutils.h"
36#include "talk/p2p/base/constants.h"
37#include "talk/p2p/base/p2ptransport.h"
38#include "talk/p2p/base/parsing.h"
39#include "talk/p2p/base/sessionclient.h"
40#include "talk/p2p/base/sessiondescription.h"
41#include "talk/p2p/base/transport.h"
42#include "talk/xmllite/xmlconstants.h"
43#include "talk/xmpp/constants.h"
44
45namespace cricket {
46
47ActionType 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;
80
81 return ACTION_UNKNOWN;
82}
83
84std::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_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;
103 default:
104 return "";
105 }
106}
107
108std::string ToGingleString(ActionType type) {
109 switch (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;
122 default:
123 return "";
124 }
125}
126
127
128bool 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) && jingle->HasAttr(QN_SID));
134}
135
136bool IsGingleMessage(const buzz::XmlElement* stanza) {
137 const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION);
138 if (session == NULL)
139 return false;
140
141 return (session->HasAttr(buzz::QN_TYPE) &&
142 session->HasAttr(buzz::QN_ID) &&
143 session->HasAttr(QN_INITIATOR));
144}
145
146bool 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)));
151}
152
153bool ParseGingleSessionMessage(const buzz::XmlElement* session,
154 SessionMessage* msg,
155 ParseError* error) {
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;
162
163 if (msg->type == ACTION_UNKNOWN)
164 return BadParse("unknown action: " + type_string, error);
165
166 return true;
167}
168
169bool ParseJingleSessionMessage(const buzz::XmlElement* jingle,
170 SessionMessage* msg,
171 ParseError* error) {
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;
178
179 if (msg->type == ACTION_UNKNOWN)
180 return BadParse("unknown action: " + type_string, error);
181
182 return true;
183}
184
185bool ParseHybridSessionMessage(const buzz::XmlElement* jingle,
186 SessionMessage* msg,
187 ParseError* error) {
188 if (!ParseJingleSessionMessage(jingle, msg, error))
189 return false;
190 msg->protocol = PROTOCOL_HYBRID;
191
192 return true;
193}
194
195bool ParseSessionMessage(const buzz::XmlElement* stanza,
196 SessionMessage* msg,
197 ParseError* error) {
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;
202
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);
207 if (jingle != NULL)
208 return ParseJingleSessionMessage(jingle, msg, error);
209 if (session != NULL)
210 return ParseGingleSessionMessage(session, msg, error);
211 return false;
212}
213
214buzz::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);
221 return session;
222}
223
224buzz::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);
231 }
232 AddXmlChildren(jingle, action_elems);
233 return jingle;
234}
235
236void 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);
241
242 if (msg.protocol == PROTOCOL_GINGLE) {
243 stanza->AddElement(WriteGingleAction(msg, action_elems));
244 } else {
245 stanza->AddElement(WriteJingleAction(msg, action_elems));
246 }
247}
248
249
250TransportParser* 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()) {
254 return NULL;
255 } else {
256 return map->second;
257 }
258}
259
260CandidateTranslator* 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()) {
265 return NULL;
266 } else {
267 return map->second;
268 }
269}
270
271bool 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,
277 ParseError* error) {
278 *parser = GetTransportParser(trans_parsers, transport_type);
279 if (*parser == NULL) {
280 return BadParse("unknown transport type: " + transport_type, error);
281 }
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);
287 return true;
288}
289
290bool 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,
296 WriteError* error) {
297 *parser = GetTransportParser(trans_parsers, transport_type);
298 if (*parser == NULL) {
299 return BadWrite("unknown transport type: " + transport_type, error);
300 }
301 *translator = GetCandidateTranslator(translators, content_name);
302 if (*translator == NULL) {
303 return BadWrite("unknown content name: " + content_name, error);
304 }
305 return true;
306}
307
308bool ParseGingleCandidate(const buzz::XmlElement* candidate_elem,
309 const TransportParserMap& trans_parsers,
310 const CandidateTranslatorMap& translators,
311 const std::string& content_name,
312 Candidates* candidates,
313 ParseError* error) {
314 TransportParser* trans_parser;
315 CandidateTranslator* translator;
316 if (!GetParserAndTranslator(trans_parsers, translators,
317 NS_GINGLE_P2P, content_name,
318 &trans_parser, &translator, error))
319 return false;
320
321 Candidate candidate;
322 if (!trans_parser->ParseGingleCandidate(
323 candidate_elem, translator, &candidate, error)) {
324 return false;
325 }
326
327 candidates->push_back(candidate);
328 return true;
329}
330
331bool ParseGingleCandidates(const buzz::XmlElement* parent,
332 const TransportParserMap& trans_parsers,
333 const CandidateTranslatorMap& translators,
334 const std::string& content_name,
335 Candidates* candidates,
336 ParseError* error) {
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)) {
343 return false;
344 }
345 }
346 }
347 return true;
348}
349
350bool ParseGingleTransportInfos(const buzz::XmlElement* action_elem,
351 const ContentInfos& contents,
352 const TransportParserMap& trans_parsers,
353 const CandidateTranslatorMap& translators,
354 TransportInfos* tinfos,
355 ParseError* error) {
356 bool has_audio = FindContentInfoByName(contents, CN_AUDIO) != NULL;
357 bool has_video = FindContentInfoByName(contents, CN_VIDEO) != NULL;
358
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, Candidates()));
363 if (!ParseGingleCandidates(action_elem, trans_parsers, translators,
364 CN_OTHER, &tinfo.description.candidates,
365 error)) {
366 return false;
367 }
368
369 tinfos->push_back(tinfo);
370 return true;
371 }
372
373 // If we have media, separate the candidates.
374 TransportInfo audio_tinfo(CN_AUDIO,
375 TransportDescription(NS_GINGLE_P2P, Candidates()));
376 TransportInfo video_tinfo(CN_VIDEO,
377 TransportDescription(NS_GINGLE_P2P, Candidates()));
378 for (const buzz::XmlElement* candidate_elem = action_elem->FirstElement();
379 candidate_elem != NULL;
380 candidate_elem = candidate_elem->NextElement()) {
381 if (candidate_elem->Name().LocalPart() == LN_CANDIDATE) {
382 const std::string& channel_name = candidate_elem->Attr(buzz::QN_NAME);
383 if (has_audio &&
384 (channel_name == GICE_CHANNEL_NAME_RTP ||
385 channel_name == GICE_CHANNEL_NAME_RTCP)) {
386 if (!ParseGingleCandidate(
387 candidate_elem, trans_parsers,
388 translators, CN_AUDIO,
389 &audio_tinfo.description.candidates, error)) {
390 return false;
391 }
392 } else if (has_video &&
393 (channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
394 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP)) {
395 if (!ParseGingleCandidate(
396 candidate_elem, trans_parsers,
397 translators, CN_VIDEO,
398 &video_tinfo.description.candidates, error)) {
399 return false;
400 }
401 } else {
402 return BadParse("Unknown channel name: " + channel_name, error);
403 }
404 }
405 }
406
407 if (has_audio) {
408 tinfos->push_back(audio_tinfo);
409 }
410 if (has_video) {
411 tinfos->push_back(video_tinfo);
412 }
413 return true;
414}
415
416bool ParseJingleTransportInfo(const buzz::XmlElement* trans_elem,
417 const std::string& content_name,
418 const TransportParserMap& trans_parsers,
419 const CandidateTranslatorMap& translators,
420 TransportInfo* tinfo,
421 ParseError* error) {
422 TransportParser* trans_parser;
423 CandidateTranslator* translator;
424 if (!GetParserAndTranslator(trans_parsers, translators,
425 trans_elem->Name().Namespace(), content_name,
426 &trans_parser, &translator, error))
427 return false;
428
429 TransportDescription tdesc;
430 if (!trans_parser->ParseTransportDescription(trans_elem, translator,
431 &tdesc, error))
432 return false;
433
434 *tinfo = TransportInfo(content_name, tdesc);
435 return true;
436}
437
438bool ParseJingleTransportInfos(const buzz::XmlElement* jingle,
439 const ContentInfos& contents,
440 const TransportParserMap trans_parsers,
441 const CandidateTranslatorMap& translators,
442 TransportInfos* tinfos,
443 ParseError* error) {
444 for (const buzz::XmlElement* pair_elem
445 = jingle->FirstNamed(QN_JINGLE_CONTENT);
446 pair_elem != NULL;
447 pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) {
448 std::string content_name;
449 if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME,
450 &content_name, error))
451 return false;
452
453 const ContentInfo* content = FindContentInfoByName(contents, content_name);
454 if (!content)
455 return BadParse("Unknown content name: " + content_name, error);
456
457 const buzz::XmlElement* trans_elem;
458 if (!RequireXmlChild(pair_elem, LN_TRANSPORT, &trans_elem, error))
459 return false;
460
461 TransportInfo tinfo;
462 if (!ParseJingleTransportInfo(trans_elem, content->name,
463 trans_parsers, translators,
464 &tinfo, error))
465 return false;
466
467 tinfos->push_back(tinfo);
468 }
469
470 return true;
471}
472
473buzz::XmlElement* NewTransportElement(const std::string& name) {
474 return new buzz::XmlElement(buzz::QName(name, LN_TRANSPORT), true);
475}
476
477bool WriteGingleCandidates(const Candidates& candidates,
478 const TransportParserMap& trans_parsers,
479 const std::string& transport_type,
480 const CandidateTranslatorMap& translators,
481 const std::string& content_name,
482 XmlElements* elems,
483 WriteError* error) {
484 TransportParser* trans_parser;
485 CandidateTranslator* translator;
486 if (!GetParserAndTranslator(trans_parsers, translators,
487 transport_type, content_name,
488 &trans_parser, &translator, error))
489 return false;
490
491 for (size_t i = 0; i < candidates.size(); ++i) {
492 talk_base::scoped_ptr<buzz::XmlElement> element;
493 if (!trans_parser->WriteGingleCandidate(candidates[i], translator,
494 element.accept(), error)) {
495 return false;
496 }
497
498 elems->push_back(element.release());
499 }
500
501 return true;
502}
503
504bool WriteGingleTransportInfos(const TransportInfos& tinfos,
505 const TransportParserMap& trans_parsers,
506 const CandidateTranslatorMap& translators,
507 XmlElements* elems,
508 WriteError* error) {
509 for (TransportInfos::const_iterator tinfo = tinfos.begin();
510 tinfo != tinfos.end(); ++tinfo) {
511 if (!WriteGingleCandidates(tinfo->description.candidates,
512 trans_parsers, tinfo->description.transport_type,
513 translators, tinfo->content_name,
514 elems, error))
515 return false;
516 }
517
518 return true;
519}
520
521bool WriteJingleTransportInfo(const TransportInfo& tinfo,
522 const TransportParserMap& trans_parsers,
523 const CandidateTranslatorMap& translators,
524 XmlElements* elems,
525 WriteError* error) {
526 std::string transport_type = tinfo.description.transport_type;
527 TransportParser* trans_parser;
528 CandidateTranslator* translator;
529 if (!GetParserAndTranslator(trans_parsers, translators,
530 transport_type, tinfo.content_name,
531 &trans_parser, &translator, error))
532 return false;
533
534 buzz::XmlElement* trans_elem;
535 if (!trans_parser->WriteTransportDescription(tinfo.description, translator,
536 &trans_elem, error)) {
537 return false;
538 }
539
540 elems->push_back(trans_elem);
541 return true;
542}
543
544void WriteJingleContent(const std::string name,
545 const XmlElements& child_elems,
546 XmlElements* elems) {
547 buzz::XmlElement* content_elem = new buzz::XmlElement(QN_JINGLE_CONTENT);
548 content_elem->SetAttr(QN_JINGLE_CONTENT_NAME, name);
549 content_elem->SetAttr(QN_CREATOR, LN_INITIATOR);
550 AddXmlChildren(content_elem, child_elems);
551
552 elems->push_back(content_elem);
553}
554
555bool WriteJingleTransportInfos(const TransportInfos& tinfos,
556 const TransportParserMap& trans_parsers,
557 const CandidateTranslatorMap& translators,
558 XmlElements* elems,
559 WriteError* error) {
560 for (TransportInfos::const_iterator tinfo = tinfos.begin();
561 tinfo != tinfos.end(); ++tinfo) {
562 XmlElements content_child_elems;
563 if (!WriteJingleTransportInfo(*tinfo, trans_parsers, translators,
564 &content_child_elems, error))
565
566 return false;
567
568 WriteJingleContent(tinfo->content_name, content_child_elems, elems);
569 }
570
571 return true;
572}
573
574ContentParser* GetContentParser(const ContentParserMap& content_parsers,
575 const std::string& type) {
576 ContentParserMap::const_iterator map = content_parsers.find(type);
577 if (map == content_parsers.end()) {
578 return NULL;
579 } else {
580 return map->second;
581 }
582}
583
584bool ParseContentInfo(SignalingProtocol protocol,
585 const std::string& name,
586 const std::string& type,
587 const buzz::XmlElement* elem,
588 const ContentParserMap& parsers,
589 ContentInfos* contents,
590 ParseError* error) {
591 ContentParser* parser = GetContentParser(parsers, type);
592 if (parser == NULL)
593 return BadParse("unknown application content: " + type, error);
594
595 ContentDescription* desc;
596 if (!parser->ParseContent(protocol, elem, &desc, error))
597 return false;
598
599 contents->push_back(ContentInfo(name, type, desc));
600 return true;
601}
602
603bool ParseContentType(const buzz::XmlElement* parent_elem,
604 std::string* content_type,
605 const buzz::XmlElement** content_elem,
606 ParseError* error) {
607 if (!RequireXmlChild(parent_elem, LN_DESCRIPTION, content_elem, error))
608 return false;
609
610 *content_type = (*content_elem)->Name().Namespace();
611 return true;
612}
613
614bool ParseGingleContentInfos(const buzz::XmlElement* session,
615 const ContentParserMap& content_parsers,
616 ContentInfos* contents,
617 ParseError* error) {
618 std::string content_type;
619 const buzz::XmlElement* content_elem;
620 if (!ParseContentType(session, &content_type, &content_elem, error))
621 return false;
622
623 if (content_type == NS_GINGLE_VIDEO) {
624 // A parser parsing audio or video content should look at the
625 // namespace and only parse the codecs relevant to that namespace.
626 // We use this to control which codecs get parsed: first audio,
627 // then video.
628 talk_base::scoped_ptr<buzz::XmlElement> audio_elem(
629 new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT));
630 CopyXmlChildren(content_elem, audio_elem.get());
631 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP,
632 audio_elem.get(), content_parsers,
633 contents, error))
634 return false;
635
636 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_VIDEO, NS_JINGLE_RTP,
637 content_elem, content_parsers,
638 contents, error))
639 return false;
640 } else if (content_type == NS_GINGLE_AUDIO) {
641 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP,
642 content_elem, content_parsers,
643 contents, error))
644 return false;
645 } else {
646 if (!ParseContentInfo(PROTOCOL_GINGLE, CN_OTHER, content_type,
647 content_elem, content_parsers,
648 contents, error))
649 return false;
650 }
651 return true;
652}
653
654bool ParseJingleContentInfos(const buzz::XmlElement* jingle,
655 const ContentParserMap& content_parsers,
656 ContentInfos* contents,
657 ParseError* error) {
658 for (const buzz::XmlElement* pair_elem
659 = jingle->FirstNamed(QN_JINGLE_CONTENT);
660 pair_elem != NULL;
661 pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) {
662 std::string content_name;
663 if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME,
664 &content_name, error))
665 return false;
666
667 std::string content_type;
668 const buzz::XmlElement* content_elem;
669 if (!ParseContentType(pair_elem, &content_type, &content_elem, error))
670 return false;
671
672 if (!ParseContentInfo(PROTOCOL_JINGLE, content_name, content_type,
673 content_elem, content_parsers,
674 contents, error))
675 return false;
676 }
677 return true;
678}
679
680bool ParseJingleGroupInfos(const buzz::XmlElement* jingle,
681 ContentGroups* groups,
682 ParseError* error) {
683 for (const buzz::XmlElement* pair_elem
684 = jingle->FirstNamed(QN_JINGLE_DRAFT_GROUP);
685 pair_elem != NULL;
686 pair_elem = pair_elem->NextNamed(QN_JINGLE_DRAFT_GROUP)) {
687 std::string group_name;
688 if (!RequireXmlAttr(pair_elem, QN_JINGLE_DRAFT_GROUP_TYPE,
689 &group_name, error))
690 return false;
691
692 ContentGroup group(group_name);
693 for (const buzz::XmlElement* child_elem
694 = pair_elem->FirstNamed(QN_JINGLE_CONTENT);
695 child_elem != NULL;
696 child_elem = child_elem->NextNamed(QN_JINGLE_CONTENT)) {
697 std::string content_name;
698 if (!RequireXmlAttr(child_elem, QN_JINGLE_CONTENT_NAME,
699 &content_name, error))
700 return false;
701 group.AddContentName(content_name);
702 }
703 groups->push_back(group);
704 }
705 return true;
706}
707
708buzz::XmlElement* WriteContentInfo(SignalingProtocol protocol,
709 const ContentInfo& content,
710 const ContentParserMap& parsers,
711 WriteError* error) {
712 ContentParser* parser = GetContentParser(parsers, content.type);
713 if (parser == NULL) {
714 BadWrite("unknown content type: " + content.type, error);
715 return NULL;
716 }
717
718 buzz::XmlElement* elem = NULL;
719 if (!parser->WriteContent(protocol, content.description, &elem, error))
720 return NULL;
721
722 return elem;
723}
724
725bool IsWritable(SignalingProtocol protocol,
726 const ContentInfo& content,
727 const ContentParserMap& parsers) {
728 ContentParser* parser = GetContentParser(parsers, content.type);
729 if (parser == NULL) {
730 return false;
731 }
732
733 return parser->IsWritable(protocol, content.description);
734}
735
736bool WriteGingleContentInfos(const ContentInfos& contents,
737 const ContentParserMap& parsers,
738 XmlElements* elems,
739 WriteError* error) {
740 if (contents.size() == 1 ||
741 (contents.size() == 2 &&
742 !IsWritable(PROTOCOL_GINGLE, contents.at(1), parsers))) {
743 if (contents.front().rejected) {
744 return BadWrite("Gingle protocol may not reject individual contents.",
745 error);
746 }
747 buzz::XmlElement* elem = WriteContentInfo(
748 PROTOCOL_GINGLE, contents.front(), parsers, error);
749 if (!elem)
750 return false;
751
752 elems->push_back(elem);
753 } else if (contents.size() >= 2 &&
754 contents.at(0).type == NS_JINGLE_RTP &&
755 contents.at(1).type == NS_JINGLE_RTP) {
756 // Special-case audio + video contents so that they are "merged"
757 // into one "video" content.
758 if (contents.at(0).rejected || contents.at(1).rejected) {
759 return BadWrite("Gingle protocol may not reject individual contents.",
760 error);
761 }
762 buzz::XmlElement* audio = WriteContentInfo(
763 PROTOCOL_GINGLE, contents.at(0), parsers, error);
764 if (!audio)
765 return false;
766
767 buzz::XmlElement* video = WriteContentInfo(
768 PROTOCOL_GINGLE, contents.at(1), parsers, error);
769 if (!video) {
770 delete audio;
771 return false;
772 }
773
774 CopyXmlChildren(audio, video);
775 elems->push_back(video);
776 delete audio;
777 } else {
778 return BadWrite("Gingle protocol may only have one content.", error);
779 }
780
781 return true;
782}
783
784const TransportInfo* GetTransportInfoByContentName(
785 const TransportInfos& tinfos, const std::string& content_name) {
786 for (TransportInfos::const_iterator tinfo = tinfos.begin();
787 tinfo != tinfos.end(); ++tinfo) {
788 if (content_name == tinfo->content_name) {
789 return &*tinfo;
790 }
791 }
792 return NULL;
793}
794
795bool WriteJingleContents(const ContentInfos& contents,
796 const ContentParserMap& content_parsers,
797 const TransportInfos& tinfos,
798 const TransportParserMap& trans_parsers,
799 const CandidateTranslatorMap& translators,
800 XmlElements* elems,
801 WriteError* error) {
802 for (ContentInfos::const_iterator content = contents.begin();
803 content != contents.end(); ++content) {
804 if (content->rejected) {
805 continue;
806 }
807 const TransportInfo* tinfo =
808 GetTransportInfoByContentName(tinfos, content->name);
809 if (!tinfo)
810 return BadWrite("No transport for content: " + content->name, error);
811
812 XmlElements pair_elems;
813 buzz::XmlElement* elem = WriteContentInfo(
814 PROTOCOL_JINGLE, *content, content_parsers, error);
815 if (!elem)
816 return false;
817 pair_elems.push_back(elem);
818
819 if (!WriteJingleTransportInfo(*tinfo, trans_parsers, translators,
820 &pair_elems, error))
821 return false;
822
823 WriteJingleContent(content->name, pair_elems, elems);
824 }
825 return true;
826}
827
828bool WriteJingleContentInfos(const ContentInfos& contents,
829 const ContentParserMap& content_parsers,
830 XmlElements* elems,
831 WriteError* error) {
832 for (ContentInfos::const_iterator content = contents.begin();
833 content != contents.end(); ++content) {
834 if (content->rejected) {
835 continue;
836 }
837 XmlElements content_child_elems;
838 buzz::XmlElement* elem = WriteContentInfo(
839 PROTOCOL_JINGLE, *content, content_parsers, error);
840 if (!elem)
841 return false;
842 content_child_elems.push_back(elem);
843 WriteJingleContent(content->name, content_child_elems, elems);
844 }
845 return true;
846}
847
848bool WriteJingleGroupInfo(const ContentInfos& contents,
849 const ContentGroups& groups,
850 XmlElements* elems,
851 WriteError* error) {
852 if (!groups.empty()) {
853 buzz::XmlElement* pair_elem = new buzz::XmlElement(QN_JINGLE_DRAFT_GROUP);
854 pair_elem->SetAttr(QN_JINGLE_DRAFT_GROUP_TYPE, GROUP_TYPE_BUNDLE);
855
856 XmlElements pair_elems;
857 for (ContentInfos::const_iterator content = contents.begin();
858 content != contents.end(); ++content) {
859 buzz::XmlElement* child_elem =
860 new buzz::XmlElement(QN_JINGLE_CONTENT, false);
861 child_elem->SetAttr(QN_JINGLE_CONTENT_NAME, content->name);
862 pair_elems.push_back(child_elem);
863 }
864 AddXmlChildren(pair_elem, pair_elems);
865 elems->push_back(pair_elem);
866 }
867 return true;
868}
869
870bool ParseContentType(SignalingProtocol protocol,
871 const buzz::XmlElement* action_elem,
872 std::string* content_type,
873 ParseError* error) {
874 const buzz::XmlElement* content_elem;
875 if (protocol == PROTOCOL_GINGLE) {
876 if (!ParseContentType(action_elem, content_type, &content_elem, error))
877 return false;
878
879 // Internally, we only use NS_JINGLE_RTP.
880 if (*content_type == NS_GINGLE_AUDIO ||
881 *content_type == NS_GINGLE_VIDEO)
882 *content_type = NS_JINGLE_RTP;
883 } else {
884 const buzz::XmlElement* pair_elem
885 = action_elem->FirstNamed(QN_JINGLE_CONTENT);
886 if (pair_elem == NULL)
887 return BadParse("No contents found", error);
888
889 if (!ParseContentType(pair_elem, content_type, &content_elem, error))
890 return false;
891 }
892
893 return true;
894}
895
896static bool ParseContentMessage(
897 SignalingProtocol protocol,
898 const buzz::XmlElement* action_elem,
899 bool expect_transports,
900 const ContentParserMap& content_parsers,
901 const TransportParserMap& trans_parsers,
902 const CandidateTranslatorMap& translators,
903 SessionInitiate* init,
904 ParseError* error) {
905 init->owns_contents = true;
906 if (protocol == PROTOCOL_GINGLE) {
907 if (!ParseGingleContentInfos(action_elem, content_parsers,
908 &init->contents, error))
909 return false;
910
911 if (expect_transports &&
912 !ParseGingleTransportInfos(action_elem, init->contents,
913 trans_parsers, translators,
914 &init->transports, error))
915 return false;
916 } else {
917 if (!ParseJingleContentInfos(action_elem, content_parsers,
918 &init->contents, error))
919 return false;
920 if (!ParseJingleGroupInfos(action_elem, &init->groups, error))
921 return false;
922
923 if (expect_transports &&
924 !ParseJingleTransportInfos(action_elem, init->contents,
925 trans_parsers, translators,
926 &init->transports, error))
927 return false;
928 }
929
930 return true;
931}
932
933static bool WriteContentMessage(
934 SignalingProtocol protocol,
935 const ContentInfos& contents,
936 const TransportInfos& tinfos,
937 const ContentParserMap& content_parsers,
938 const TransportParserMap& transport_parsers,
939 const CandidateTranslatorMap& translators,
940 const ContentGroups& groups,
941 XmlElements* elems,
942 WriteError* error) {
943 if (protocol == PROTOCOL_GINGLE) {
944 if (!WriteGingleContentInfos(contents, content_parsers, elems, error))
945 return false;
946
947 if (!WriteGingleTransportInfos(tinfos, transport_parsers, translators,
948 elems, error))
949 return false;
950 } else {
951 if (!WriteJingleContents(contents, content_parsers,
952 tinfos, transport_parsers, translators,
953 elems, error))
954 return false;
955 if (!WriteJingleGroupInfo(contents, groups, elems, error))
956 return false;
957 }
958
959 return true;
960}
961
962bool ParseSessionInitiate(SignalingProtocol protocol,
963 const buzz::XmlElement* action_elem,
964 const ContentParserMap& content_parsers,
965 const TransportParserMap& trans_parsers,
966 const CandidateTranslatorMap& translators,
967 SessionInitiate* init,
968 ParseError* error) {
969 bool expect_transports = true;
970 return ParseContentMessage(protocol, action_elem, expect_transports,
971 content_parsers, trans_parsers, translators,
972 init, error);
973}
974
975
976bool WriteSessionInitiate(SignalingProtocol protocol,
977 const ContentInfos& contents,
978 const TransportInfos& tinfos,
979 const ContentParserMap& content_parsers,
980 const TransportParserMap& transport_parsers,
981 const CandidateTranslatorMap& translators,
982 const ContentGroups& groups,
983 XmlElements* elems,
984 WriteError* error) {
985 return WriteContentMessage(protocol, contents, tinfos,
986 content_parsers, transport_parsers, translators,
987 groups,
988 elems, error);
989}
990
991bool ParseSessionAccept(SignalingProtocol protocol,
992 const buzz::XmlElement* action_elem,
993 const ContentParserMap& content_parsers,
994 const TransportParserMap& transport_parsers,
995 const CandidateTranslatorMap& translators,
996 SessionAccept* accept,
997 ParseError* error) {
998 bool expect_transports = true;
999 return ParseContentMessage(protocol, action_elem, expect_transports,
1000 content_parsers, transport_parsers, translators,
1001 accept, error);
1002}
1003
1004bool WriteSessionAccept(SignalingProtocol protocol,
1005 const ContentInfos& contents,
1006 const TransportInfos& tinfos,
1007 const ContentParserMap& content_parsers,
1008 const TransportParserMap& transport_parsers,
1009 const CandidateTranslatorMap& translators,
1010 const ContentGroups& groups,
1011 XmlElements* elems,
1012 WriteError* error) {
1013 return WriteContentMessage(protocol, contents, tinfos,
1014 content_parsers, transport_parsers, translators,
1015 groups,
1016 elems, error);
1017}
1018
1019bool ParseSessionTerminate(SignalingProtocol protocol,
1020 const buzz::XmlElement* action_elem,
1021 SessionTerminate* term,
1022 ParseError* error) {
1023 if (protocol == PROTOCOL_GINGLE) {
1024 const buzz::XmlElement* reason_elem = action_elem->FirstElement();
1025 if (reason_elem != NULL) {
1026 term->reason = reason_elem->Name().LocalPart();
1027 const buzz::XmlElement *debug_elem = reason_elem->FirstElement();
1028 if (debug_elem != NULL) {
1029 term->debug_reason = debug_elem->Name().LocalPart();
1030 }
1031 }
1032 return true;
1033 } else {
1034 const buzz::XmlElement* reason_elem =
1035 action_elem->FirstNamed(QN_JINGLE_REASON);
1036 if (reason_elem) {
1037 reason_elem = reason_elem->FirstElement();
1038 if (reason_elem) {
1039 term->reason = reason_elem->Name().LocalPart();
1040 }
1041 }
1042 return true;
1043 }
1044}
1045
1046void WriteSessionTerminate(SignalingProtocol protocol,
1047 const SessionTerminate& term,
1048 XmlElements* elems) {
1049 if (protocol == PROTOCOL_GINGLE) {
1050 elems->push_back(new buzz::XmlElement(buzz::QName(NS_GINGLE, term.reason)));
1051 } else {
1052 if (!term.reason.empty()) {
1053 buzz::XmlElement* reason_elem = new buzz::XmlElement(QN_JINGLE_REASON);
1054 reason_elem->AddElement(new buzz::XmlElement(
1055 buzz::QName(NS_JINGLE, term.reason)));
1056 elems->push_back(reason_elem);
1057 }
1058 }
1059}
1060
1061bool ParseDescriptionInfo(SignalingProtocol protocol,
1062 const buzz::XmlElement* action_elem,
1063 const ContentParserMap& content_parsers,
1064 const TransportParserMap& transport_parsers,
1065 const CandidateTranslatorMap& translators,
1066 DescriptionInfo* description_info,
1067 ParseError* error) {
1068 bool expect_transports = false;
1069 return ParseContentMessage(protocol, action_elem, expect_transports,
1070 content_parsers, transport_parsers, translators,
1071 description_info, error);
1072}
1073
1074bool WriteDescriptionInfo(SignalingProtocol protocol,
1075 const ContentInfos& contents,
1076 const ContentParserMap& content_parsers,
1077 XmlElements* elems,
1078 WriteError* error) {
1079 if (protocol == PROTOCOL_GINGLE) {
1080 return WriteGingleContentInfos(contents, content_parsers, elems, error);
1081 } else {
1082 return WriteJingleContentInfos(contents, content_parsers, elems, error);
1083 }
1084}
1085
1086bool ParseTransportInfos(SignalingProtocol protocol,
1087 const buzz::XmlElement* action_elem,
1088 const ContentInfos& contents,
1089 const TransportParserMap& trans_parsers,
1090 const CandidateTranslatorMap& translators,
1091 TransportInfos* tinfos,
1092 ParseError* error) {
1093 if (protocol == PROTOCOL_GINGLE) {
1094 return ParseGingleTransportInfos(
1095 action_elem, contents, trans_parsers, translators, tinfos, error);
1096 } else {
1097 return ParseJingleTransportInfos(
1098 action_elem, contents, trans_parsers, translators, tinfos, error);
1099 }
1100}
1101
1102bool WriteTransportInfos(SignalingProtocol protocol,
1103 const TransportInfos& tinfos,
1104 const TransportParserMap& trans_parsers,
1105 const CandidateTranslatorMap& translators,
1106 XmlElements* elems,
1107 WriteError* error) {
1108 if (protocol == PROTOCOL_GINGLE) {
1109 return WriteGingleTransportInfos(tinfos, trans_parsers, translators,
1110 elems, error);
1111 } else {
1112 return WriteJingleTransportInfos(tinfos, trans_parsers, translators,
1113 elems, error);
1114 }
1115}
1116
1117bool GetUriTarget(const std::string& prefix, const std::string& str,
1118 std::string* after) {
1119 size_t pos = str.find(prefix);
1120 if (pos == std::string::npos)
1121 return false;
1122
1123 *after = str.substr(pos + prefix.size(), std::string::npos);
1124 return true;
1125}
1126
1127bool FindSessionRedirect(const buzz::XmlElement* stanza,
1128 SessionRedirect* redirect) {
1129 const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR);
1130 if (error_elem == NULL)
1131 return false;
1132
1133 const buzz::XmlElement* redirect_elem =
1134 error_elem->FirstNamed(QN_GINGLE_REDIRECT);
1135 if (redirect_elem == NULL)
1136 redirect_elem = error_elem->FirstNamed(buzz::QN_STANZA_REDIRECT);
1137 if (redirect_elem == NULL)
1138 return false;
1139
1140 if (!GetUriTarget(STR_REDIRECT_PREFIX, redirect_elem->BodyText(),
1141 &redirect->target))
1142 return false;
1143
1144 return true;
1145}
1146
1147} // namespace cricket