Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2010, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "talk/p2p/base/sessionmessages.h"
     29 
     30 #include <stdio.h>
     31 #include <string>
     32 
     33 #include "talk/p2p/base/constants.h"
     34 #include "talk/p2p/base/p2ptransport.h"
     35 #include "talk/p2p/base/parsing.h"
     36 #include "talk/p2p/base/sessionclient.h"
     37 #include "talk/p2p/base/sessiondescription.h"
     38 #include "talk/p2p/base/transport.h"
     39 #include "webrtc/libjingle/xmllite/xmlconstants.h"
     40 #include "talk/xmpp/constants.h"
     41 #include "webrtc/base/logging.h"
     42 #include "webrtc/base/scoped_ptr.h"
     43 #include "webrtc/base/stringutils.h"
     44 
     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
   1150