• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/xmllite/xmlconstants.h"
36 #include "talk/xmpp/constants.h"
37 #include "talk/p2p/base/constants.h"
38 #include "talk/p2p/base/p2ptransport.h"
39 #include "talk/p2p/base/parsing.h"
40 #include "talk/p2p/base/sessionclient.h"
41 #include "talk/p2p/base/sessiondescription.h"
42 #include "talk/p2p/base/transport.h"
43 #include "talk/xmllite/xmlconstants.h"
44 
45 namespace cricket {
46 
ToActionType(const std::string & type)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 == GINGLE_ACTION_NOTIFY)
77     return ACTION_NOTIFY;
78   if (type == GINGLE_ACTION_UPDATE)
79     return ACTION_UPDATE;
80 
81   return ACTION_UNKNOWN;
82 }
83 
ToJingleString(ActionType type)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_SESSION_ACCEPT:
91       return JINGLE_ACTION_SESSION_ACCEPT;
92     // Notice that reject and terminate both go to
93     // "session-terminate", but there is no "session-reject".
94     case ACTION_SESSION_REJECT:
95     case ACTION_SESSION_TERMINATE:
96       return JINGLE_ACTION_SESSION_TERMINATE;
97     case ACTION_TRANSPORT_INFO:
98       return JINGLE_ACTION_TRANSPORT_INFO;
99     case ACTION_TRANSPORT_ACCEPT:
100       return JINGLE_ACTION_TRANSPORT_ACCEPT;
101     default:
102       return "";
103   }
104 }
105 
ToGingleString(ActionType type)106 std::string ToGingleString(ActionType type) {
107   switch (type) {
108     case ACTION_SESSION_INITIATE:
109       return GINGLE_ACTION_INITIATE;
110     case ACTION_SESSION_INFO:
111       return GINGLE_ACTION_INFO;
112     case ACTION_SESSION_ACCEPT:
113       return GINGLE_ACTION_ACCEPT;
114     case ACTION_SESSION_REJECT:
115       return GINGLE_ACTION_REJECT;
116     case ACTION_SESSION_TERMINATE:
117       return GINGLE_ACTION_TERMINATE;
118     case ACTION_VIEW:
119       return GINGLE_ACTION_VIEW;
120     case ACTION_TRANSPORT_INFO:
121       return GINGLE_ACTION_CANDIDATES;
122     default:
123       return "";
124   }
125 }
126 
127 
IsJingleMessage(const buzz::XmlElement * stanza)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) &&
134           (jingle->HasAttr(QN_SID)
135            // TODO: This works around a bug in old jingle
136            // clients that set QN_ID instead of QN_SID.  Once we know
137            // there are no clients which have this bug, we can remove
138            // this code.
139            || jingle->HasAttr(QN_ID)));
140 }
141 
IsGingleMessage(const buzz::XmlElement * stanza)142 bool IsGingleMessage(const buzz::XmlElement* stanza) {
143   const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION);
144   if (session == NULL)
145     return false;
146 
147   return (session->HasAttr(buzz::QN_TYPE) &&
148           session->HasAttr(buzz::QN_ID)   &&
149           session->HasAttr(QN_INITIATOR));
150 }
151 
IsSessionMessage(const buzz::XmlElement * stanza)152 bool IsSessionMessage(const buzz::XmlElement* stanza) {
153   return (stanza->Name() == buzz::QN_IQ &&
154           stanza->Attr(buzz::QN_TYPE) == buzz::STR_SET &&
155           (IsJingleMessage(stanza) ||
156            IsGingleMessage(stanza)));
157 }
158 
ParseGingleSessionMessage(const buzz::XmlElement * session,SessionMessage * msg,ParseError * error)159 bool ParseGingleSessionMessage(const buzz::XmlElement* session,
160                                SessionMessage* msg,
161                                ParseError* error) {
162   msg->protocol = PROTOCOL_GINGLE;
163   std::string type_string = session->Attr(buzz::QN_TYPE);
164   msg->type = ToActionType(type_string);
165   msg->sid = session->Attr(buzz::QN_ID);
166   msg->initiator = session->Attr(QN_INITIATOR);
167   msg->action_elem = session;
168 
169   if (msg->type == ACTION_UNKNOWN)
170     return BadParse("unknown action: " + type_string, error);
171 
172   return true;
173 }
174 
ParseJingleSessionMessage(const buzz::XmlElement * jingle,SessionMessage * msg,ParseError * error)175 bool ParseJingleSessionMessage(const buzz::XmlElement* jingle,
176                                SessionMessage* msg,
177                                ParseError* error) {
178   msg->protocol = PROTOCOL_JINGLE;
179   std::string type_string = jingle->Attr(buzz::QN_ACTION);
180   msg->type = ToActionType(type_string);
181   msg->sid = jingle->Attr(QN_SID);
182   // TODO: This works around a bug in old jingle clients
183   // that set QN_ID instead of QN_SID.  Once we know there are no
184   // clients which have this bug, we can remove this code.
185   if (msg->sid.empty()) {
186     msg->sid = jingle->Attr(buzz::QN_ID);
187   }
188   msg->initiator = GetXmlAttr(jingle, QN_INITIATOR, buzz::STR_EMPTY);
189   msg->action_elem = jingle;
190 
191   if (msg->type == ACTION_UNKNOWN)
192     return BadParse("unknown action: " + type_string, error);
193 
194   return true;
195 }
196 
ParseHybridSessionMessage(const buzz::XmlElement * jingle,SessionMessage * msg,ParseError * error)197 bool ParseHybridSessionMessage(const buzz::XmlElement* jingle,
198                                SessionMessage* msg,
199                                ParseError* error) {
200   if (!ParseJingleSessionMessage(jingle, msg, error))
201     return false;
202   msg->protocol = PROTOCOL_HYBRID;
203 
204   return true;
205 }
206 
ParseSessionMessage(const buzz::XmlElement * stanza,SessionMessage * msg,ParseError * error)207 bool ParseSessionMessage(const buzz::XmlElement* stanza,
208                          SessionMessage* msg,
209                          ParseError* error) {
210   msg->id = stanza->Attr(buzz::QN_ID);
211   msg->from = stanza->Attr(buzz::QN_FROM);
212   msg->to = stanza->Attr(buzz::QN_TO);
213   msg->stanza = stanza;
214 
215   const buzz::XmlElement* jingle = stanza->FirstNamed(QN_JINGLE);
216   const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION);
217   if (jingle && session)
218     return ParseHybridSessionMessage(jingle, msg, error);
219   if (jingle != NULL)
220     return ParseJingleSessionMessage(jingle, msg, error);
221   if (session != NULL)
222     return ParseGingleSessionMessage(session, msg, error);
223   return false;
224 }
225 
WriteGingleAction(const SessionMessage & msg,const XmlElements & action_elems)226 buzz::XmlElement* WriteGingleAction(const SessionMessage& msg,
227                                     const XmlElements& action_elems) {
228   buzz::XmlElement* session = new buzz::XmlElement(QN_GINGLE_SESSION, true);
229   session->AddAttr(buzz::QN_TYPE, ToGingleString(msg.type));
230   session->AddAttr(buzz::QN_ID, msg.sid);
231   session->AddAttr(QN_INITIATOR, msg.initiator);
232   AddXmlChildren(session, action_elems);
233   return session;
234 }
235 
WriteJingleAction(const SessionMessage & msg,const XmlElements & action_elems)236 buzz::XmlElement* WriteJingleAction(const SessionMessage& msg,
237                                     const XmlElements& action_elems) {
238   buzz::XmlElement* jingle = new buzz::XmlElement(QN_JINGLE, true);
239   jingle->AddAttr(buzz::QN_ACTION, ToJingleString(msg.type));
240   jingle->AddAttr(QN_SID, msg.sid);
241   // TODO: This works around a bug in old jingle clinets
242   // that expected QN_ID instead of QN_SID.  Once we know there are no
243   // clients which have this bug, we can remove this code.
244   jingle->AddAttr(QN_ID, msg.sid);
245   // TODO: Right now, the XMPP server rejects a jingle-only
246   // (non hybrid) message with "feature-not-implemented" if there is
247   // no initiator.  Fix the server, and then only set the initiator on
248   // session-initiate messages here.
249   jingle->AddAttr(QN_INITIATOR, msg.initiator);
250   AddXmlChildren(jingle, action_elems);
251   return jingle;
252 }
253 
WriteSessionMessage(const SessionMessage & msg,const XmlElements & action_elems,buzz::XmlElement * stanza)254 void WriteSessionMessage(const SessionMessage& msg,
255                          const XmlElements& action_elems,
256                          buzz::XmlElement* stanza) {
257   stanza->SetAttr(buzz::QN_TO, msg.to);
258   stanza->SetAttr(buzz::QN_TYPE, buzz::STR_SET);
259 
260   if (msg.protocol == PROTOCOL_GINGLE) {
261     stanza->AddElement(WriteGingleAction(msg, action_elems));
262   } else {
263     stanza->AddElement(WriteJingleAction(msg, action_elems));
264   }
265 }
266 
267 
GetTransportParser(const TransportParserMap & trans_parsers,const std::string & name)268 TransportParser* GetTransportParser(const TransportParserMap& trans_parsers,
269                                     const std::string& name) {
270   TransportParserMap::const_iterator map = trans_parsers.find(name);
271   if (map == trans_parsers.end()) {
272     return NULL;
273   } else {
274     return map->second;
275   }
276 }
277 
ParseCandidates(SignalingProtocol protocol,const buzz::XmlElement * candidates_elem,const TransportParserMap & trans_parsers,const std::string & transport_type,Candidates * candidates,ParseError * error)278 bool ParseCandidates(SignalingProtocol protocol,
279                      const buzz::XmlElement* candidates_elem,
280                      const TransportParserMap& trans_parsers,
281                      const std::string& transport_type,
282                      Candidates* candidates,
283                      ParseError* error) {
284   TransportParser* trans_parser =
285       GetTransportParser(trans_parsers, transport_type);
286   if (trans_parser == NULL)
287     return BadParse("unknown transport type: " + transport_type, error);
288 
289   return trans_parser->ParseCandidates(protocol, candidates_elem,
290                                        candidates, error);
291 }
292 
ParseGingleTransportInfos(const buzz::XmlElement * action_elem,const ContentInfos & contents,const TransportParserMap & trans_parsers,TransportInfos * tinfos,ParseError * error)293 bool ParseGingleTransportInfos(const buzz::XmlElement* action_elem,
294                                const ContentInfos& contents,
295                                const TransportParserMap& trans_parsers,
296                                TransportInfos* tinfos,
297                                ParseError* error) {
298   TransportInfo tinfo(CN_OTHER, NS_GINGLE_P2P, Candidates());
299   if (!ParseCandidates(PROTOCOL_GINGLE, action_elem,
300                        trans_parsers, NS_GINGLE_P2P,
301                        &tinfo.candidates, error))
302     return false;
303 
304   bool has_audio = FindContentInfoByName(contents, CN_AUDIO) != NULL;
305   bool has_video = FindContentInfoByName(contents, CN_VIDEO) != NULL;
306 
307   // If we don't have media, no need to separate the candidates.
308   if (!has_audio && !has_audio) {
309     tinfos->push_back(tinfo);
310     return true;
311   }
312 
313   // If we have media, separate the candidates.  Create the
314   // TransportInfo here to avoid copying the candidates.
315   TransportInfo audio_tinfo(CN_AUDIO, NS_GINGLE_P2P, Candidates());
316   TransportInfo video_tinfo(CN_VIDEO, NS_GINGLE_P2P, Candidates());
317   for (Candidates::iterator cand = tinfo.candidates.begin();
318        cand != tinfo.candidates.end(); cand++) {
319     if (cand->name() == GINGLE_CANDIDATE_NAME_RTP ||
320         cand->name() == GINGLE_CANDIDATE_NAME_RTCP) {
321       audio_tinfo.candidates.push_back(*cand);
322     } else if (cand->name() == GINGLE_CANDIDATE_NAME_VIDEO_RTP ||
323                cand->name() == GINGLE_CANDIDATE_NAME_VIDEO_RTCP) {
324       video_tinfo.candidates.push_back(*cand);
325     }
326   }
327 
328   if (has_audio) {
329     tinfos->push_back(audio_tinfo);
330   }
331 
332   if (has_video) {
333     tinfos->push_back(video_tinfo);
334   }
335 
336   return true;
337 }
338 
ParseJingleTransportInfo(const buzz::XmlElement * trans_elem,const ContentInfo & content,const TransportParserMap & trans_parsers,TransportInfos * tinfos,ParseError * error)339 bool ParseJingleTransportInfo(const buzz::XmlElement* trans_elem,
340                               const ContentInfo& content,
341                               const TransportParserMap& trans_parsers,
342                               TransportInfos* tinfos,
343                               ParseError* error) {
344   std::string transport_type = trans_elem->Name().Namespace();
345   TransportInfo tinfo(content.name, transport_type, Candidates());
346   if (!ParseCandidates(PROTOCOL_JINGLE, trans_elem,
347                        trans_parsers, transport_type,
348                        &tinfo.candidates, error))
349     return false;
350 
351   tinfos->push_back(tinfo);
352   return true;
353 }
354 
ParseJingleTransportInfos(const buzz::XmlElement * jingle,const ContentInfos & contents,const TransportParserMap trans_parsers,TransportInfos * tinfos,ParseError * error)355 bool ParseJingleTransportInfos(const buzz::XmlElement* jingle,
356                                const ContentInfos& contents,
357                                const TransportParserMap trans_parsers,
358                                TransportInfos* tinfos,
359                                ParseError* error) {
360   for (const buzz::XmlElement* pair_elem
361            = jingle->FirstNamed(QN_JINGLE_CONTENT);
362        pair_elem != NULL;
363        pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) {
364     std::string content_name;
365     if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME,
366                         &content_name, error))
367       return false;
368 
369     const ContentInfo* content = FindContentInfoByName(contents, content_name);
370     if (!content)
371       return BadParse("Unknown content name: " + content_name, error);
372 
373     const buzz::XmlElement* trans_elem;
374     if (!RequireXmlChild(pair_elem, LN_TRANSPORT, &trans_elem, error))
375       return false;
376 
377     if (!ParseJingleTransportInfo(trans_elem, *content, trans_parsers,
378                                   tinfos, error))
379       return false;
380   }
381 
382   return true;
383 }
384 
NewTransportElement(const std::string & name)385 buzz::XmlElement* NewTransportElement(const std::string& name) {
386   return new buzz::XmlElement(buzz::QName(true, name, LN_TRANSPORT), true);
387 }
388 
WriteCandidates(SignalingProtocol protocol,const std::string & trans_type,const Candidates & candidates,const TransportParserMap & trans_parsers,XmlElements * elems,WriteError * error)389 bool WriteCandidates(SignalingProtocol protocol,
390                      const std::string& trans_type,
391                      const Candidates& candidates,
392                      const TransportParserMap& trans_parsers,
393                      XmlElements* elems,
394                      WriteError* error) {
395   TransportParser* trans_parser = GetTransportParser(trans_parsers, trans_type);
396   if (trans_parser == NULL)
397     return BadWrite("unknown transport type: " + trans_type, error);
398 
399   return trans_parser->WriteCandidates(protocol, candidates, elems, error);
400 }
401 
WriteGingleTransportInfos(const TransportInfos & tinfos,const TransportParserMap & trans_parsers,XmlElements * elems,WriteError * error)402 bool WriteGingleTransportInfos(const TransportInfos& tinfos,
403                                const TransportParserMap& trans_parsers,
404                                XmlElements* elems,
405                                WriteError* error) {
406   for (TransportInfos::const_iterator tinfo = tinfos.begin();
407        tinfo != tinfos.end(); ++tinfo) {
408     if (!WriteCandidates(PROTOCOL_GINGLE,
409                          tinfo->transport_type, tinfo->candidates,
410                          trans_parsers, elems, error))
411       return false;
412   }
413 
414   return true;
415 }
416 
WriteJingleTransportInfo(const TransportInfo & tinfo,const TransportParserMap & trans_parsers,XmlElements * elems,WriteError * error)417 bool WriteJingleTransportInfo(const TransportInfo& tinfo,
418                               const TransportParserMap& trans_parsers,
419                               XmlElements* elems,
420                               WriteError* error) {
421   XmlElements candidate_elems;
422   if (!WriteCandidates(PROTOCOL_JINGLE,
423                        tinfo.transport_type, tinfo.candidates, trans_parsers,
424                        &candidate_elems, error))
425     return false;
426 
427   buzz::XmlElement* trans_elem = NewTransportElement(tinfo.transport_type);
428   AddXmlChildren(trans_elem, candidate_elems);
429   elems->push_back(trans_elem);
430   return true;
431 }
432 
WriteJingleContentPair(const std::string name,const XmlElements & pair_elems,XmlElements * elems)433 void WriteJingleContentPair(const std::string name,
434                             const XmlElements& pair_elems,
435                             XmlElements* elems) {
436   buzz::XmlElement* pair_elem = new buzz::XmlElement(QN_JINGLE_CONTENT);
437   pair_elem->SetAttr(QN_JINGLE_CONTENT_NAME, name);
438   pair_elem->SetAttr(QN_CREATOR, LN_INITIATOR);
439   AddXmlChildren(pair_elem, pair_elems);
440 
441   elems->push_back(pair_elem);
442 }
443 
WriteJingleTransportInfos(const TransportInfos & tinfos,const TransportParserMap & trans_parsers,XmlElements * elems,WriteError * error)444 bool WriteJingleTransportInfos(const TransportInfos& tinfos,
445                                const TransportParserMap& trans_parsers,
446                                XmlElements* elems,
447                                WriteError* error) {
448   for (TransportInfos::const_iterator tinfo = tinfos.begin();
449        tinfo != tinfos.end(); ++tinfo) {
450     XmlElements pair_elems;
451     if (!WriteJingleTransportInfo(*tinfo, trans_parsers,
452                                   &pair_elems, error))
453       return false;
454 
455     WriteJingleContentPair(tinfo->content_name, pair_elems, elems);
456   }
457 
458   return true;
459 }
460 
GetContentParser(const ContentParserMap & content_parsers,const std::string & type)461 ContentParser* GetContentParser(const ContentParserMap& content_parsers,
462                                 const std::string& type) {
463   ContentParserMap::const_iterator map = content_parsers.find(type);
464   if (map == content_parsers.end()) {
465     return NULL;
466   } else {
467     return map->second;
468   }
469 }
470 
ParseContentInfo(SignalingProtocol protocol,const std::string & name,const std::string & type,const buzz::XmlElement * elem,const ContentParserMap & parsers,ContentInfos * contents,ParseError * error)471 bool ParseContentInfo(SignalingProtocol protocol,
472                       const std::string& name,
473                       const std::string& type,
474                       const buzz::XmlElement* elem,
475                       const ContentParserMap& parsers,
476                       ContentInfos* contents,
477                       ParseError* error) {
478   ContentParser* parser = GetContentParser(parsers, type);
479   if (parser == NULL)
480     return BadParse("unknown application content: " + type, error);
481 
482   const ContentDescription* desc;
483   if (!parser->ParseContent(protocol, elem, &desc, error))
484     return false;
485 
486   contents->push_back(ContentInfo(name, type, desc));
487   return true;
488 }
489 
ParseContentType(const buzz::XmlElement * parent_elem,std::string * content_type,const buzz::XmlElement ** content_elem,ParseError * error)490 bool ParseContentType(const buzz::XmlElement* parent_elem,
491                       std::string* content_type,
492                       const buzz::XmlElement** content_elem,
493                       ParseError* error) {
494   if (!RequireXmlChild(parent_elem, LN_DESCRIPTION, content_elem, error))
495     return false;
496 
497   *content_type = (*content_elem)->Name().Namespace();
498   return true;
499 }
500 
ParseGingleContentInfos(const buzz::XmlElement * session,const ContentParserMap & content_parsers,ContentInfos * contents,ParseError * error)501 bool ParseGingleContentInfos(const buzz::XmlElement* session,
502                              const ContentParserMap& content_parsers,
503                              ContentInfos* contents,
504                              ParseError* error) {
505   std::string content_type;
506   const buzz::XmlElement* content_elem;
507   if (!ParseContentType(session, &content_type, &content_elem, error))
508     return false;
509 
510   if (content_type == NS_GINGLE_VIDEO) {
511     // A parser parsing audio or video content should look at the
512     // namespace and only parse the codecs relevant to that namespace.
513     // We use this to control which codecs get parsed: first audio,
514     // then video.
515     talk_base::scoped_ptr<buzz::XmlElement> audio_elem(
516         new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT));
517     CopyXmlChildren(content_elem, audio_elem.get());
518     if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP,
519                           audio_elem.get(), content_parsers,
520                           contents, error))
521       return false;
522 
523     if (!ParseContentInfo(PROTOCOL_GINGLE, CN_VIDEO, NS_JINGLE_RTP,
524                           content_elem, content_parsers,
525                           contents, error))
526       return false;
527   } else if (content_type == NS_GINGLE_AUDIO) {
528     if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP,
529                           content_elem, content_parsers,
530                           contents, error))
531       return false;
532   } else {
533     if (!ParseContentInfo(PROTOCOL_GINGLE, CN_OTHER, content_type,
534                           content_elem, content_parsers,
535                           contents, error))
536       return false;
537   }
538   return true;
539 }
540 
ParseJingleContentInfos(const buzz::XmlElement * jingle,const ContentParserMap & content_parsers,ContentInfos * contents,ParseError * error)541 bool ParseJingleContentInfos(const buzz::XmlElement* jingle,
542                              const ContentParserMap& content_parsers,
543                              ContentInfos* contents,
544                              ParseError* error) {
545   for (const buzz::XmlElement* pair_elem
546            = jingle->FirstNamed(QN_JINGLE_CONTENT);
547        pair_elem != NULL;
548        pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) {
549     std::string content_name;
550     if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME,
551                         &content_name, error))
552       return false;
553 
554     std::string content_type;
555     const buzz::XmlElement* content_elem;
556     if (!ParseContentType(pair_elem, &content_type, &content_elem, error))
557       return false;
558 
559     if (!ParseContentInfo(PROTOCOL_JINGLE, content_name, content_type,
560                           content_elem, content_parsers,
561                           contents, error))
562       return false;
563   }
564   return true;
565 }
566 
WriteContentInfo(SignalingProtocol protocol,const ContentInfo & content,const ContentParserMap & parsers,WriteError * error)567 buzz::XmlElement* WriteContentInfo(SignalingProtocol protocol,
568                                    const ContentInfo& content,
569                                    const ContentParserMap& parsers,
570                                    WriteError* error) {
571   ContentParser* parser = GetContentParser(parsers, content.type);
572   if (parser == NULL) {
573     BadWrite("unknown content type: " + content.type, error);
574     return NULL;
575   }
576 
577   buzz::XmlElement* elem = NULL;
578   if (!parser->WriteContent(protocol, content.description, &elem, error))
579     return NULL;
580 
581   return elem;
582 }
583 
WriteGingleContentInfos(const ContentInfos & contents,const ContentParserMap & parsers,XmlElements * elems,WriteError * error)584 bool WriteGingleContentInfos(const ContentInfos& contents,
585                              const ContentParserMap& parsers,
586                              XmlElements* elems,
587                              WriteError* error) {
588   if (contents.size() == 1) {
589     buzz::XmlElement* elem = WriteContentInfo(
590         PROTOCOL_GINGLE, contents.front(), parsers, error);
591     if (!elem)
592       return false;
593 
594     elems->push_back(elem);
595   } else if (contents.size() == 2 &&
596              contents.at(0).type == NS_JINGLE_RTP &&
597              contents.at(1).type == NS_JINGLE_RTP) {
598      // Special-case audio + video contents so that they are "merged"
599      // into one "video" content.
600     buzz::XmlElement* audio = WriteContentInfo(
601         PROTOCOL_GINGLE, contents.at(0), parsers, error);
602     if (!audio)
603       return false;
604 
605     buzz::XmlElement* video = WriteContentInfo(
606         PROTOCOL_GINGLE, contents.at(1), parsers, error);
607     if (!video) {
608       delete audio;
609       return false;
610     }
611 
612     CopyXmlChildren(audio, video);
613     elems->push_back(video);
614     delete audio;
615   } else {
616     return BadWrite("Gingle protocol may only have one content.", error);
617   }
618 
619   return true;
620 }
621 
GetTransportInfoByContentName(const TransportInfos & tinfos,const std::string & content_name)622 const TransportInfo* GetTransportInfoByContentName(
623     const TransportInfos& tinfos, const std::string& content_name) {
624   for (TransportInfos::const_iterator tinfo = tinfos.begin();
625        tinfo != tinfos.end(); ++tinfo) {
626     if (content_name == tinfo->content_name) {
627       return &*tinfo;
628     }
629   }
630   return NULL;
631 }
632 
WriteJingleContentPairs(const ContentInfos & contents,const ContentParserMap & content_parsers,const TransportInfos & tinfos,const TransportParserMap & trans_parsers,XmlElements * elems,WriteError * error)633 bool WriteJingleContentPairs(const ContentInfos& contents,
634                              const ContentParserMap& content_parsers,
635                              const TransportInfos& tinfos,
636                              const TransportParserMap& trans_parsers,
637                              XmlElements* elems,
638                              WriteError* error) {
639   for (ContentInfos::const_iterator content = contents.begin();
640        content != contents.end(); ++content) {
641     const TransportInfo* tinfo =
642         GetTransportInfoByContentName(tinfos, content->name);
643     if (!tinfo)
644       return BadWrite("No transport for content: " + content->name, error);
645 
646     XmlElements pair_elems;
647     buzz::XmlElement* elem = WriteContentInfo(
648         PROTOCOL_JINGLE, *content, content_parsers, error);
649     if (!elem)
650       return false;
651     pair_elems.push_back(elem);
652 
653     if (!WriteJingleTransportInfo(*tinfo, trans_parsers,
654                                   &pair_elems, error))
655       return false;
656 
657     WriteJingleContentPair(content->name, pair_elems, elems);
658   }
659   return true;
660 }
661 
ParseContentType(SignalingProtocol protocol,const buzz::XmlElement * action_elem,std::string * content_type,ParseError * error)662 bool ParseContentType(SignalingProtocol protocol,
663                       const buzz::XmlElement* action_elem,
664                       std::string* content_type,
665                       ParseError* error) {
666   const buzz::XmlElement* content_elem;
667   if (protocol == PROTOCOL_GINGLE) {
668     if (!ParseContentType(action_elem, content_type, &content_elem, error))
669       return false;
670 
671     // Internally, we only use NS_JINGLE_RTP.
672     if (*content_type == NS_GINGLE_AUDIO ||
673         *content_type == NS_GINGLE_VIDEO)
674       *content_type = NS_JINGLE_RTP;
675   } else {
676     const buzz::XmlElement* pair_elem
677         = action_elem->FirstNamed(QN_JINGLE_CONTENT);
678     if (pair_elem == NULL)
679       return BadParse("No contents found", error);
680 
681     if (!ParseContentType(pair_elem, content_type, &content_elem, error))
682       return false;
683 
684     // If there is more than one content type, return an error.
685     for (; pair_elem != NULL;
686          pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) {
687       std::string content_type2;
688       if (!ParseContentType(pair_elem, &content_type2, &content_elem, error))
689         return false;
690 
691       if (content_type2 != *content_type)
692         return BadParse("More than one content type found", error);
693     }
694   }
695 
696   return true;
697 }
698 
ParseSessionInitiate(SignalingProtocol protocol,const buzz::XmlElement * action_elem,const ContentParserMap & content_parsers,const TransportParserMap & trans_parsers,SessionInitiate * init,ParseError * error)699 bool ParseSessionInitiate(SignalingProtocol protocol,
700                           const buzz::XmlElement* action_elem,
701                           const ContentParserMap& content_parsers,
702                           const TransportParserMap& trans_parsers,
703                           SessionInitiate* init,
704                           ParseError* error) {
705   init->owns_contents = true;
706   if (protocol == PROTOCOL_GINGLE) {
707     if (!ParseGingleContentInfos(action_elem, content_parsers,
708                                  &init->contents, error))
709       return false;
710 
711     if (!ParseGingleTransportInfos(action_elem, init->contents, trans_parsers,
712                                    &init->transports, error))
713       return false;
714   } else {
715     if (!ParseJingleContentInfos(action_elem, content_parsers,
716                                  &init->contents, error))
717       return false;
718 
719     if (!ParseJingleTransportInfos(action_elem, init->contents, trans_parsers,
720                                    &init->transports, error))
721       return false;
722   }
723 
724   return true;
725 }
726 
727 
WriteSessionInitiate(SignalingProtocol protocol,const ContentInfos & contents,const TransportInfos & tinfos,const ContentParserMap & content_parsers,const TransportParserMap & transport_parsers,XmlElements * elems,WriteError * error)728 bool WriteSessionInitiate(SignalingProtocol protocol,
729                           const ContentInfos& contents,
730                           const TransportInfos& tinfos,
731                           const ContentParserMap& content_parsers,
732                           const TransportParserMap& transport_parsers,
733                           XmlElements* elems,
734                           WriteError* error) {
735   if (protocol == PROTOCOL_GINGLE) {
736     if (!WriteGingleContentInfos(contents, content_parsers, elems, error))
737       return false;
738 
739     if (!WriteGingleTransportInfos(tinfos, transport_parsers,
740                                    elems, error))
741       return false;
742   } else {
743     if (!WriteJingleContentPairs(contents, content_parsers,
744                                  tinfos, transport_parsers,
745                                  elems, error))
746       return false;
747   }
748 
749   return true;
750 }
751 
ParseSessionAccept(SignalingProtocol protocol,const buzz::XmlElement * action_elem,const ContentParserMap & content_parsers,const TransportParserMap & transport_parsers,SessionAccept * accept,ParseError * error)752 bool ParseSessionAccept(SignalingProtocol protocol,
753                         const buzz::XmlElement* action_elem,
754                         const ContentParserMap& content_parsers,
755                         const TransportParserMap& transport_parsers,
756                         SessionAccept* accept,
757                         ParseError* error) {
758   return ParseSessionInitiate(protocol, action_elem,
759                               content_parsers, transport_parsers,
760                               accept, error);
761 }
762 
WriteSessionAccept(SignalingProtocol protocol,const ContentInfos & contents,const TransportInfos & tinfos,const ContentParserMap & content_parsers,const TransportParserMap & transport_parsers,XmlElements * elems,WriteError * error)763 bool WriteSessionAccept(SignalingProtocol protocol,
764                         const ContentInfos& contents,
765                         const TransportInfos& tinfos,
766                         const ContentParserMap& content_parsers,
767                         const TransportParserMap& transport_parsers,
768                         XmlElements* elems,
769                         WriteError* error) {
770   return WriteSessionInitiate(protocol, contents, tinfos,
771                               content_parsers, transport_parsers,
772                               elems, error);
773 }
774 
ParseSessionTerminate(SignalingProtocol protocol,const buzz::XmlElement * action_elem,SessionTerminate * term,ParseError * error)775 bool ParseSessionTerminate(SignalingProtocol protocol,
776                            const buzz::XmlElement* action_elem,
777                            SessionTerminate* term,
778                            ParseError* error) {
779   if (protocol == PROTOCOL_GINGLE) {
780     const buzz::XmlElement* reason_elem = action_elem->FirstElement();
781     if (reason_elem != NULL) {
782       term->reason = reason_elem->Name().LocalPart();
783       const buzz::XmlElement *debug_elem = reason_elem->FirstElement();
784       if (debug_elem != NULL) {
785         term->debug_reason = debug_elem->Name().LocalPart();
786       }
787     }
788     return true;
789   } else {
790     const buzz::XmlElement* reason_elem =
791         action_elem->FirstNamed(QN_JINGLE_REASON);
792     if (reason_elem) {
793       reason_elem = reason_elem->FirstElement();
794       if (reason_elem) {
795         term->reason = reason_elem->Name().LocalPart();
796       }
797     }
798     return true;
799   }
800 }
801 
WriteSessionTerminate(SignalingProtocol protocol,const SessionTerminate & term,XmlElements * elems)802 void WriteSessionTerminate(SignalingProtocol protocol,
803                            const SessionTerminate& term,
804                            XmlElements* elems) {
805   if (protocol == PROTOCOL_GINGLE) {
806     elems->push_back(new buzz::XmlElement(
807         buzz::QName(true, NS_GINGLE, term.reason)));
808   } else {
809     if (!term.reason.empty()) {
810       buzz::XmlElement* reason_elem = new buzz::XmlElement(QN_JINGLE_REASON);
811       reason_elem->AddElement(new buzz::XmlElement(
812           buzz::QName(true, NS_JINGLE, term.reason)));
813       elems->push_back(reason_elem);
814     }
815   }
816 }
817 
ParseTransportInfos(SignalingProtocol protocol,const buzz::XmlElement * action_elem,const ContentInfos & contents,const TransportParserMap & trans_parsers,TransportInfos * tinfos,ParseError * error)818 bool ParseTransportInfos(SignalingProtocol protocol,
819                          const buzz::XmlElement* action_elem,
820                          const ContentInfos& contents,
821                          const TransportParserMap& trans_parsers,
822                          TransportInfos* tinfos,
823                          ParseError* error) {
824   if (protocol == PROTOCOL_GINGLE) {
825     return ParseGingleTransportInfos(
826         action_elem, contents, trans_parsers, tinfos, error);
827   } else {
828     return ParseJingleTransportInfos(
829         action_elem, contents, trans_parsers, tinfos, error);
830   }
831 }
832 
WriteTransportInfos(SignalingProtocol protocol,const TransportInfos & tinfos,const TransportParserMap & trans_parsers,XmlElements * elems,WriteError * error)833 bool WriteTransportInfos(SignalingProtocol protocol,
834                          const TransportInfos& tinfos,
835                          const TransportParserMap& trans_parsers,
836                          XmlElements* elems,
837                          WriteError* error) {
838   if (protocol == PROTOCOL_GINGLE) {
839     return WriteGingleTransportInfos(tinfos, trans_parsers,
840                                      elems, error);
841   } else {
842     return WriteJingleTransportInfos(tinfos, trans_parsers,
843                                      elems, error);
844   }
845 }
846 
ParseSessionNotify(const buzz::XmlElement * action_elem,SessionNotify * notify,ParseError * error)847 bool ParseSessionNotify(const buzz::XmlElement* action_elem,
848                         SessionNotify* notify, ParseError* error) {
849   const buzz::XmlElement* notify_elem;
850   for (notify_elem = action_elem->FirstNamed(QN_GINGLE_NOTIFY);
851       notify_elem != NULL;
852       notify_elem = notify_elem->NextNamed(QN_GINGLE_NOTIFY)) {
853     // Note that a subsequent notify element for the same user will override a
854     // previous.  We don't merge them.
855     std::string nick(notify_elem->Attr(QN_GINGLE_NOTIFY_NICK));
856     if (nick != buzz::STR_EMPTY) {
857       MediaSources sources;
858       const buzz::XmlElement* source_elem;
859       for (source_elem = notify_elem->FirstNamed(QN_GINGLE_NOTIFY_SOURCE);
860           source_elem != NULL;
861           source_elem = source_elem->NextNamed(QN_GINGLE_NOTIFY_SOURCE)) {
862         std::string ssrc = source_elem->Attr(QN_GINGLE_NOTIFY_SOURCE_SSRC);
863         if (ssrc != buzz::STR_EMPTY) {
864           std::string mtype = source_elem->Attr(QN_GINGLE_NOTIFY_SOURCE_MTYPE);
865           if (mtype == GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO) {
866             sources.audio_ssrc = strtoul(ssrc.c_str(), NULL, 10);
867           } else if (mtype == GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO) {
868             sources.video_ssrc = strtoul(ssrc.c_str(), NULL, 10);
869           }
870         }
871       }
872 
873       notify->nickname_to_sources.insert(
874           std::pair<std::string, MediaSources>(nick, sources));
875     }
876   }
877 
878   return true;
879 }
880 
GetUriTarget(const std::string & prefix,const std::string & str,std::string * after)881 bool GetUriTarget(const std::string& prefix, const std::string& str,
882                   std::string* after) {
883   size_t pos = str.find(prefix);
884   if (pos == std::string::npos)
885     return false;
886 
887   *after = str.substr(pos + prefix.size(), std::string::npos);
888   return true;
889 }
890 
ParseSessionUpdate(const buzz::XmlElement * action_elem,SessionUpdate * update,ParseError * error)891 bool ParseSessionUpdate(const buzz::XmlElement* action_elem,
892                         SessionUpdate* update, ParseError* error) {
893   // TODO: Parse the update message.
894   return true;
895 }
896 
WriteSessionView(const SessionView & view,XmlElements * elems)897 void WriteSessionView(const SessionView& view, XmlElements* elems) {
898   std::vector<VideoViewRequest>::const_iterator it;
899   for (it = view.view_requests.begin(); it != view.view_requests.end(); it++) {
900     talk_base::scoped_ptr<buzz::XmlElement> view_elem(
901         new buzz::XmlElement(QN_GINGLE_VIEW));
902     if (view_elem.get() == NULL) {
903       return;
904     }
905 
906     view_elem->SetAttr(QN_GINGLE_VIEW_TYPE, GINGLE_VIEW_TYPE_STATIC);
907     view_elem->SetAttr(QN_GINGLE_VIEW_NICK, it->nick_name);
908     view_elem->SetAttr(QN_GINGLE_VIEW_MEDIA_TYPE,
909         GINGLE_VIEW_MEDIA_TYPE_VIDEO);
910 
911     // A 32-bit uint, expressed as decimal, has a max of 10 digits, plus one
912     // for the null.
913     char str[11];
914     int result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->ssrc);
915     if (result < 0 || result >= ARRAY_SIZE(str)) {
916       continue;
917     }
918     view_elem->SetAttr(QN_GINGLE_VIEW_SSRC, str);
919 
920     // Include video-specific parameters in a child <params> element.
921     talk_base::scoped_ptr<buzz::XmlElement> params_elem(
922         new buzz::XmlElement(QN_GINGLE_VIEW_PARAMS));
923     if (params_elem.get() == NULL) {
924       return;
925     }
926 
927     result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->width);
928     if (result < 0 || result >= ARRAY_SIZE(str)) {
929       continue;
930     }
931     params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_WIDTH, str);
932 
933     result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->height);
934     if (result < 0 || result >= ARRAY_SIZE(str)) {
935       continue;
936     }
937     params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_HEIGHT, str);
938 
939     result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->framerate);
940     if (result < 0 || result >= ARRAY_SIZE(str)) {
941       continue;
942     }
943     params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_FRAMERATE, str);
944 
945     view_elem->AddElement(params_elem.release());
946     elems->push_back(view_elem.release());
947   }
948 }
949 
FindSessionRedirect(const buzz::XmlElement * stanza,SessionRedirect * redirect)950 bool FindSessionRedirect(const buzz::XmlElement* stanza,
951                          SessionRedirect* redirect) {
952   const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR);
953   if (error_elem == NULL)
954     return false;
955 
956   const buzz::XmlElement* redirect_elem =
957       error_elem->FirstNamed(QN_GINGLE_REDIRECT);
958   if (redirect_elem == NULL)
959     redirect_elem = error_elem->FirstNamed(buzz::QN_STANZA_REDIRECT);
960   if (redirect_elem == NULL)
961     return false;
962 
963   if (!GetUriTarget(STR_REDIRECT_PREFIX, redirect_elem->BodyText(),
964                     &redirect->target))
965     return false;
966 
967   return true;
968 }
969 
970 }  // namespace cricket
971