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 "talk/p2p/base/sessionmessages.h"
29
30#include <stdio.h>
31#include <string>
32
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"
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, std::string(), std::string()));
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(
375      CN_AUDIO,
376      TransportDescription(NS_GINGLE_P2P, std::string(), std::string()));
377  TransportInfo video_tinfo(
378      CN_VIDEO,
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);
385      if (has_audio &&
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)) {
392          return false;
393        }
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)) {
401          return false;
402        }
403      } else {
404        return BadParse("Unknown channel name: " + channel_name, error);
405      }
406    }
407  }
408
409  if (has_audio) {
410    tinfos->push_back(audio_tinfo);
411  }
412  if (has_video) {
413    tinfos->push_back(video_tinfo);
414  }
415  return true;
416}
417
418bool ParseJingleTransportInfo(const buzz::XmlElement* trans_elem,
419                              const std::string& content_name,
420                              const TransportParserMap& trans_parsers,
421                              const CandidateTranslatorMap& translators,
422                              TransportInfo* tinfo,
423                              ParseError* error) {
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))
429    return false;
430
431  TransportDescription tdesc;
432  if (!trans_parser->ParseTransportDescription(trans_elem, translator,
433                                               &tdesc, error))
434    return false;
435
436  *tinfo = TransportInfo(content_name, tdesc);
437  return true;
438}
439
440bool ParseJingleTransportInfos(const buzz::XmlElement* jingle,
441                               const ContentInfos& contents,
442                               const TransportParserMap trans_parsers,
443                               const CandidateTranslatorMap& translators,
444                               TransportInfos* tinfos,
445                               ParseError* error) {
446  for (const buzz::XmlElement* pair_elem
447           = jingle->FirstNamed(QN_JINGLE_CONTENT);
448       pair_elem != NULL;
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))
453      return false;
454
455    const ContentInfo* content = FindContentInfoByName(contents, content_name);
456    if (!content)
457      return BadParse("Unknown content name: " + content_name, error);
458
459    const buzz::XmlElement* trans_elem;
460    if (!RequireXmlChild(pair_elem, LN_TRANSPORT, &trans_elem, error))
461      return false;
462
463    TransportInfo tinfo;
464    if (!ParseJingleTransportInfo(trans_elem, content->name,
465                                  trans_parsers, translators,
466                                  &tinfo, error))
467      return false;
468
469    tinfos->push_back(tinfo);
470  }
471
472  return true;
473}
474
475buzz::XmlElement* NewTransportElement(const std::string& name) {
476  return new buzz::XmlElement(buzz::QName(name, LN_TRANSPORT), true);
477}
478
479bool 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,
484                           XmlElements* elems,
485                           WriteError* error) {
486  TransportParser* trans_parser;
487  CandidateTranslator* translator;
488  if (!GetParserAndTranslator(trans_parsers, translators,
489                              transport_type, content_name,
490                              &trans_parser, &translator, error))
491    return false;
492
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)) {
497      return false;
498    }
499
500    elems->push_back(element.release());
501  }
502
503  return true;
504}
505
506bool WriteGingleTransportInfos(const TransportInfos& tinfos,
507                               const TransportParserMap& trans_parsers,
508                               const CandidateTranslatorMap& translators,
509                               XmlElements* elems,
510                               WriteError* error) {
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,
516                               elems, error))
517      return false;
518  }
519
520  return true;
521}
522
523bool WriteJingleTransportInfo(const TransportInfo& tinfo,
524                              const TransportParserMap& trans_parsers,
525                              const CandidateTranslatorMap& translators,
526                              XmlElements* elems,
527                              WriteError* error) {
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))
534    return false;
535
536  buzz::XmlElement* trans_elem;
537  if (!trans_parser->WriteTransportDescription(tinfo.description, translator,
538                                               &trans_elem, error)) {
539    return false;
540  }
541
542  elems->push_back(trans_elem);
543  return true;
544}
545
546void 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);
553
554  elems->push_back(content_elem);
555}
556
557bool WriteJingleTransportInfos(const TransportInfos& tinfos,
558                               const TransportParserMap& trans_parsers,
559                               const CandidateTranslatorMap& translators,
560                               XmlElements* elems,
561                               WriteError* error) {
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))
567
568      return false;
569
570    WriteJingleContent(tinfo->content_name, content_child_elems, elems);
571  }
572
573  return true;
574}
575
576ContentParser* 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()) {
580    return NULL;
581  } else {
582    return map->second;
583  }
584}
585
586bool 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,
592                      ParseError* error) {
593  ContentParser* parser = GetContentParser(parsers, type);
594  if (parser == NULL)
595    return BadParse("unknown application content: " + type, error);
596
597  ContentDescription* desc;
598  if (!parser->ParseContent(protocol, elem, &desc, error))
599    return false;
600
601  contents->push_back(ContentInfo(name, type, desc));
602  return true;
603}
604
605bool ParseContentType(const buzz::XmlElement* parent_elem,
606                      std::string* content_type,
607                      const buzz::XmlElement** content_elem,
608                      ParseError* error) {
609  if (!RequireXmlChild(parent_elem, LN_DESCRIPTION, content_elem, error))
610    return false;
611
612  *content_type = (*content_elem)->Name().Namespace();
613  return true;
614}
615
616bool ParseGingleContentInfos(const buzz::XmlElement* session,
617                             const ContentParserMap& content_parsers,
618                             ContentInfos* contents,
619                             ParseError* error) {
620  std::string content_type;
621  const buzz::XmlElement* content_elem;
622  if (!ParseContentType(session, &content_type, &content_elem, error))
623    return false;
624
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,
629    // then video.
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,
635                          contents, error))
636      return false;
637
638    if (!ParseContentInfo(PROTOCOL_GINGLE, CN_VIDEO, NS_JINGLE_RTP,
639                          content_elem, content_parsers,
640                          contents, error))
641      return false;
642  } else if (content_type == NS_GINGLE_AUDIO) {
643    if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP,
644                          content_elem, content_parsers,
645                          contents, error))
646      return false;
647  } else {
648    if (!ParseContentInfo(PROTOCOL_GINGLE, CN_OTHER, content_type,
649                          content_elem, content_parsers,
650                          contents, error))
651      return false;
652  }
653  return true;
654}
655
656bool ParseJingleContentInfos(const buzz::XmlElement* jingle,
657                             const ContentParserMap& content_parsers,
658                             ContentInfos* contents,
659                             ParseError* error) {
660  for (const buzz::XmlElement* pair_elem
661           = jingle->FirstNamed(QN_JINGLE_CONTENT);
662       pair_elem != NULL;
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))
667      return false;
668
669    std::string content_type;
670    const buzz::XmlElement* content_elem;
671    if (!ParseContentType(pair_elem, &content_type, &content_elem, error))
672      return false;
673
674    if (!ParseContentInfo(PROTOCOL_JINGLE, content_name, content_type,
675                          content_elem, content_parsers,
676                          contents, error))
677      return false;
678  }
679  return true;
680}
681
682bool ParseJingleGroupInfos(const buzz::XmlElement* jingle,
683                           ContentGroups* groups,
684                           ParseError* error) {
685  for (const buzz::XmlElement* pair_elem
686           = jingle->FirstNamed(QN_JINGLE_DRAFT_GROUP);
687       pair_elem != NULL;
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,
691                        &group_name, error))
692      return false;
693
694    ContentGroup group(group_name);
695    for (const buzz::XmlElement* child_elem
696             = pair_elem->FirstNamed(QN_JINGLE_CONTENT);
697        child_elem != NULL;
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))
702        return false;
703      group.AddContentName(content_name);
704    }
705    groups->push_back(group);
706  }
707  return true;
708}
709
710buzz::XmlElement* WriteContentInfo(SignalingProtocol protocol,
711                                   const ContentInfo& content,
712                                   const ContentParserMap& parsers,
713                                   WriteError* error) {
714  ContentParser* parser = GetContentParser(parsers, content.type);
715  if (parser == NULL) {
716    BadWrite("unknown content type: " + content.type, error);
717    return NULL;
718  }
719
720  buzz::XmlElement* elem = NULL;
721  if (!parser->WriteContent(protocol, content.description, &elem, error))
722    return NULL;
723
724  return elem;
725}
726
727bool IsWritable(SignalingProtocol protocol,
728                const ContentInfo& content,
729                const ContentParserMap& parsers) {
730  ContentParser* parser = GetContentParser(parsers, content.type);
731  if (parser == NULL) {
732    return false;
733  }
734
735  return parser->IsWritable(protocol, content.description);
736}
737
738bool WriteGingleContentInfos(const ContentInfos& contents,
739                             const ContentParserMap& parsers,
740                             XmlElements* elems,
741                             WriteError* error) {
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.",
747                      error);
748    }
749    buzz::XmlElement* elem = WriteContentInfo(
750        PROTOCOL_GINGLE, contents.front(), parsers, error);
751    if (!elem)
752      return false;
753
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.",
762                      error);
763    }
764    buzz::XmlElement* audio = WriteContentInfo(
765        PROTOCOL_GINGLE, contents.at(0), parsers, error);
766    if (!audio)
767      return false;
768
769    buzz::XmlElement* video = WriteContentInfo(
770        PROTOCOL_GINGLE, contents.at(1), parsers, error);
771    if (!video) {
772      delete audio;
773      return false;
774    }
775
776    CopyXmlChildren(audio, video);
777    elems->push_back(video);
778    delete audio;
779  } else {
780    return BadWrite("Gingle protocol may only have one content.", error);
781  }
782
783  return true;
784}
785
786const 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) {
791      return &*tinfo;
792    }
793  }
794  return NULL;
795}
796
797bool WriteJingleContents(const ContentInfos& contents,
798                         const ContentParserMap& content_parsers,
799                         const TransportInfos& tinfos,
800                         const TransportParserMap& trans_parsers,
801                         const CandidateTranslatorMap& translators,
802                         XmlElements* elems,
803                         WriteError* error) {
804  for (ContentInfos::const_iterator content = contents.begin();
805       content != contents.end(); ++content) {
806    if (content->rejected) {
807      continue;
808    }
809    const TransportInfo* tinfo =
810        GetTransportInfoByContentName(tinfos, content->name);
811    if (!tinfo)
812      return BadWrite("No transport for content: " + content->name, error);
813
814    XmlElements pair_elems;
815    buzz::XmlElement* elem = WriteContentInfo(
816        PROTOCOL_JINGLE, *content, content_parsers, error);
817    if (!elem)
818      return false;
819    pair_elems.push_back(elem);
820
821    if (!WriteJingleTransportInfo(*tinfo, trans_parsers, translators,
822                                  &pair_elems, error))
823      return false;
824
825    WriteJingleContent(content->name, pair_elems, elems);
826  }
827  return true;
828}
829
830bool WriteJingleContentInfos(const ContentInfos& contents,
831                             const ContentParserMap& content_parsers,
832                             XmlElements* elems,
833                             WriteError* error) {
834  for (ContentInfos::const_iterator content = contents.begin();
835       content != contents.end(); ++content) {
836    if (content->rejected) {
837      continue;
838    }
839    XmlElements content_child_elems;
840    buzz::XmlElement* elem = WriteContentInfo(
841        PROTOCOL_JINGLE, *content, content_parsers, error);
842    if (!elem)
843      return false;
844    content_child_elems.push_back(elem);
845    WriteJingleContent(content->name, content_child_elems, elems);
846  }
847  return true;
848}
849
850bool WriteJingleGroupInfo(const ContentInfos& contents,
851                          const ContentGroups& groups,
852                          XmlElements* elems,
853                          WriteError* error) {
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);
857
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);
865    }
866    AddXmlChildren(pair_elem, pair_elems);
867    elems->push_back(pair_elem);
868  }
869  return true;
870}
871
872bool ParseContentType(SignalingProtocol protocol,
873                      const buzz::XmlElement* action_elem,
874                      std::string* content_type,
875                      ParseError* error) {
876  const buzz::XmlElement* content_elem;
877  if (protocol == PROTOCOL_GINGLE) {
878    if (!ParseContentType(action_elem, content_type, &content_elem, error))
879      return false;
880
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;
885  } else {
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);
890
891    if (!ParseContentType(pair_elem, content_type, &content_elem, error))
892      return false;
893  }
894
895  return true;
896}
897
898static 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,
906    ParseError* error) {
907  init->owns_contents = true;
908  if (protocol == PROTOCOL_GINGLE) {
909    if (!ParseGingleContentInfos(action_elem, content_parsers,
910                                 &init->contents, error))
911      return false;
912
913    if (expect_transports &&
914        !ParseGingleTransportInfos(action_elem, init->contents,
915                                   trans_parsers, translators,
916                                   &init->transports, error))
917      return false;
918  } else {
919    if (!ParseJingleContentInfos(action_elem, content_parsers,
920                                 &init->contents, error))
921      return false;
922    if (!ParseJingleGroupInfos(action_elem, &init->groups, error))
923      return false;
924
925    if (expect_transports &&
926        !ParseJingleTransportInfos(action_elem, init->contents,
927                                   trans_parsers, translators,
928                                   &init->transports, error))
929      return false;
930  }
931
932  return true;
933}
934
935static 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,
943    XmlElements* elems,
944    WriteError* error) {
945  if (protocol == PROTOCOL_GINGLE) {
946    if (!WriteGingleContentInfos(contents, content_parsers, elems, error))
947      return false;
948
949    if (!WriteGingleTransportInfos(tinfos, transport_parsers, translators,
950                                   elems, error))
951      return false;
952  } else {
953    if (!WriteJingleContents(contents, content_parsers,
954                             tinfos, transport_parsers, translators,
955                             elems, error))
956      return false;
957    if (!WriteJingleGroupInfo(contents, groups, elems, error))
958      return false;
959  }
960
961  return true;
962}
963
964bool 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,
970                          ParseError* error) {
971  bool expect_transports = true;
972  return ParseContentMessage(protocol, action_elem, expect_transports,
973                             content_parsers, trans_parsers, translators,
974                             init, error);
975}
976
977
978bool 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,
985                          XmlElements* elems,
986                          WriteError* error) {
987  return WriteContentMessage(protocol, contents, tinfos,
988                             content_parsers, transport_parsers, translators,
989                             groups,
990                             elems, error);
991}
992
993bool 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,
999                        ParseError* error) {
1000  bool expect_transports = true;
1001  return ParseContentMessage(protocol, action_elem, expect_transports,
1002                             content_parsers, transport_parsers, translators,
1003                             accept, error);
1004}
1005
1006bool 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,
1013                        XmlElements* elems,
1014                        WriteError* error) {
1015  return WriteContentMessage(protocol, contents, tinfos,
1016                             content_parsers, transport_parsers, translators,
1017                             groups,
1018                             elems, error);
1019}
1020
1021bool 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();
1032      }
1033    }
1034    return true;
1035  } else {
1036    const buzz::XmlElement* reason_elem =
1037        action_elem->FirstNamed(QN_JINGLE_REASON);
1038    if (reason_elem) {
1039      reason_elem = reason_elem->FirstElement();
1040      if (reason_elem) {
1041        term->reason = reason_elem->Name().LocalPart();
1042      }
1043    }
1044    return true;
1045  }
1046}
1047
1048void 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)));
1053  } else {
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);
1059    }
1060  }
1061}
1062
1063bool 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);
1074}
1075
1076bool WriteDescriptionInfo(SignalingProtocol protocol,
1077                          const ContentInfos& contents,
1078                          const ContentParserMap& content_parsers,
1079                          XmlElements* elems,
1080                          WriteError* error) {
1081  if (protocol == PROTOCOL_GINGLE) {
1082    return WriteGingleContentInfos(contents, content_parsers, elems, error);
1083  } else {
1084    return WriteJingleContentInfos(contents, content_parsers, elems, error);
1085  }
1086}
1087
1088bool 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);
1098  } else {
1099    return ParseJingleTransportInfos(
1100        action_elem, contents, trans_parsers, translators, tinfos, error);
1101  }
1102}
1103
1104bool WriteTransportInfos(SignalingProtocol protocol,
1105                         const TransportInfos& tinfos,
1106                         const TransportParserMap& trans_parsers,
1107                         const CandidateTranslatorMap& translators,
1108                         XmlElements* elems,
1109                         WriteError* error) {
1110  if (protocol == PROTOCOL_GINGLE) {
1111    return WriteGingleTransportInfos(tinfos, trans_parsers, translators,
1112                                     elems, error);
1113  } else {
1114    return WriteJingleTransportInfos(tinfos, trans_parsers, translators,
1115                                     elems, error);
1116  }
1117}
1118
1119bool 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)
1123    return false;
1124
1125  *after = str.substr(pos + prefix.size(), std::string::npos);
1126  return true;
1127}
1128
1129bool FindSessionRedirect(const buzz::XmlElement* stanza,
1130                         SessionRedirect* redirect) {
1131  const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR);
1132  if (error_elem == NULL)
1133    return false;
1134
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)
1140    return false;
1141
1142  if (!GetUriTarget(STR_REDIRECT_PREFIX, redirect_elem->BodyText(),
1143                    &redirect->target))
1144    return false;
1145
1146  return true;
1147}
1148
1149}  // namespace cricket
1150