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