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 <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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    385 buzz::XmlElement* NewTransportElement(const std::string& name) {
    386   return new buzz::XmlElement(buzz::QName(true, name, LN_TRANSPORT), true);
    387 }
    388 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    891 bool ParseSessionUpdate(const buzz::XmlElement* action_elem,
    892                         SessionUpdate* update, ParseError* error) {
    893   // TODO: Parse the update message.
    894   return true;
    895 }
    896 
    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 
    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