Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / p2p / base / sessionmessages.cc
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 "webrtc/p2p/base/sessionmessages.h"
29
30 #include <stdio.h>
31 #include <string>
32
33 #include "webrtc/p2p/base/constants.h"
34 #include "webrtc/p2p/base/p2ptransport.h"
35 #include "webrtc/p2p/base/parsing.h"
36 #include "webrtc/p2p/base/sessionclient.h"
37 #include "webrtc/p2p/base/sessiondescription.h"
38 #include "webrtc/p2p/base/transport.h"
39 #include "webrtc/libjingle/xmllite/xmlconstants.h"
40 #include "webrtc/libjingle/xmpp/constants.h"
41 #include "webrtc/base/logging.h"
42 #include "webrtc/base/scoped_ptr.h"
43 #include "webrtc/base/stringutils.h"
44
45 namespace cricket {
46
47 ActionType ToActionType(const std::string& type) {
48   if (type == GINGLE_ACTION_INITIATE)
49     return ACTION_SESSION_INITIATE;
50   if (type == GINGLE_ACTION_INFO)
51     return ACTION_SESSION_INFO;
52   if (type == GINGLE_ACTION_ACCEPT)
53     return ACTION_SESSION_ACCEPT;
54   if (type == GINGLE_ACTION_REJECT)
55     return ACTION_SESSION_REJECT;
56   if (type == GINGLE_ACTION_TERMINATE)
57     return ACTION_SESSION_TERMINATE;
58   if (type == GINGLE_ACTION_CANDIDATES)
59     return ACTION_TRANSPORT_INFO;
60   if (type == JINGLE_ACTION_SESSION_INITIATE)
61     return ACTION_SESSION_INITIATE;
62   if (type == JINGLE_ACTION_TRANSPORT_INFO)
63     return ACTION_TRANSPORT_INFO;
64   if (type == JINGLE_ACTION_TRANSPORT_ACCEPT)
65     return ACTION_TRANSPORT_ACCEPT;
66   if (type == JINGLE_ACTION_SESSION_INFO)
67     return ACTION_SESSION_INFO;
68   if (type == JINGLE_ACTION_SESSION_ACCEPT)
69     return ACTION_SESSION_ACCEPT;
70   if (type == JINGLE_ACTION_SESSION_TERMINATE)
71     return ACTION_SESSION_TERMINATE;
72   if (type == JINGLE_ACTION_TRANSPORT_INFO)
73     return ACTION_TRANSPORT_INFO;
74   if (type == JINGLE_ACTION_TRANSPORT_ACCEPT)
75     return ACTION_TRANSPORT_ACCEPT;
76   if (type == 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
84 std::string ToJingleString(ActionType type) {
85   switch (type) {
86     case ACTION_SESSION_INITIATE:
87       return JINGLE_ACTION_SESSION_INITIATE;
88     case ACTION_SESSION_INFO:
89       return JINGLE_ACTION_SESSION_INFO;
90     case ACTION_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
108 std::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
128 bool IsJingleMessage(const buzz::XmlElement* stanza) {
129   const buzz::XmlElement* jingle = stanza->FirstNamed(QN_JINGLE);
130   if (jingle == NULL)
131     return false;
132
133   return (jingle->HasAttr(buzz::QN_ACTION) && jingle->HasAttr(QN_SID));
134 }
135
136 bool 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
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)));
151 }
152
153 bool 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
169 bool 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
185 bool 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
195 bool 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
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);
221   return session;
222 }
223
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);
231   }
232   AddXmlChildren(jingle, action_elems);
233   return jingle;
234 }
235
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);
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
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()) {
254     return NULL;
255   } else {
256     return map->second;
257   }
258 }
259
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()) {
265     return NULL;
266   } else {
267     return map->second;
268   }
269 }
270
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,
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
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,
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
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,
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
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,
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
350 bool 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
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,
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
440 bool 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
475 buzz::XmlElement* NewTransportElement(const std::string& name) {
476   return new buzz::XmlElement(buzz::QName(name, LN_TRANSPORT), true);
477 }
478
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,
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
506 bool 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
523 bool 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
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);
553
554   elems->push_back(content_elem);
555 }
556
557 bool 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
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()) {
580     return NULL;
581   } else {
582     return map->second;
583   }
584 }
585
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,
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
605 bool 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
616 bool 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
656 bool 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
682 bool 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
710 buzz::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
727 bool 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
738 bool 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
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) {
791       return &*tinfo;
792     }
793   }
794   return NULL;
795 }
796
797 bool 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
830 bool 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
850 bool 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
872 bool 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
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,
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
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,
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
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,
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
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,
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
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,
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
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,
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
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();
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
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)));
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
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);
1074 }
1075
1076 bool 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
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);
1098   } else {
1099     return ParseJingleTransportInfos(
1100         action_elem, contents, trans_parsers, translators, tinfos, error);
1101   }
1102 }
1103
1104 bool 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
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)
1123     return false;
1124
1125   *after = str.substr(pos + prefix.size(), std::string::npos);
1126   return true;
1127 }
1128
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)
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