| /* |
| * libjingle |
| * Copyright 2010, Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <stdio.h> |
| #include <string> |
| #include "talk/p2p/base/sessionmessages.h" |
| |
| #include "talk/base/logging.h" |
| #include "talk/base/scoped_ptr.h" |
| #include "talk/base/stringutils.h" |
| #include "talk/p2p/base/constants.h" |
| #include "talk/p2p/base/p2ptransport.h" |
| #include "talk/p2p/base/parsing.h" |
| #include "talk/p2p/base/sessionclient.h" |
| #include "talk/p2p/base/sessiondescription.h" |
| #include "talk/p2p/base/transport.h" |
| #include "talk/xmllite/xmlconstants.h" |
| #include "talk/xmpp/constants.h" |
| |
| namespace cricket { |
| |
| ActionType ToActionType(const std::string& type) { |
| if (type == GINGLE_ACTION_INITIATE) |
| return ACTION_SESSION_INITIATE; |
| if (type == GINGLE_ACTION_INFO) |
| return ACTION_SESSION_INFO; |
| if (type == GINGLE_ACTION_ACCEPT) |
| return ACTION_SESSION_ACCEPT; |
| if (type == GINGLE_ACTION_REJECT) |
| return ACTION_SESSION_REJECT; |
| if (type == GINGLE_ACTION_TERMINATE) |
| return ACTION_SESSION_TERMINATE; |
| if (type == GINGLE_ACTION_CANDIDATES) |
| return ACTION_TRANSPORT_INFO; |
| if (type == JINGLE_ACTION_SESSION_INITIATE) |
| return ACTION_SESSION_INITIATE; |
| if (type == JINGLE_ACTION_TRANSPORT_INFO) |
| return ACTION_TRANSPORT_INFO; |
| if (type == JINGLE_ACTION_TRANSPORT_ACCEPT) |
| return ACTION_TRANSPORT_ACCEPT; |
| if (type == JINGLE_ACTION_SESSION_INFO) |
| return ACTION_SESSION_INFO; |
| if (type == JINGLE_ACTION_SESSION_ACCEPT) |
| return ACTION_SESSION_ACCEPT; |
| if (type == JINGLE_ACTION_SESSION_TERMINATE) |
| return ACTION_SESSION_TERMINATE; |
| if (type == JINGLE_ACTION_TRANSPORT_INFO) |
| return ACTION_TRANSPORT_INFO; |
| if (type == JINGLE_ACTION_TRANSPORT_ACCEPT) |
| return ACTION_TRANSPORT_ACCEPT; |
| if (type == JINGLE_ACTION_DESCRIPTION_INFO) |
| return ACTION_DESCRIPTION_INFO; |
| if (type == GINGLE_ACTION_UPDATE) |
| return ACTION_DESCRIPTION_INFO; |
| |
| return ACTION_UNKNOWN; |
| } |
| |
| std::string ToJingleString(ActionType type) { |
| switch (type) { |
| case ACTION_SESSION_INITIATE: |
| return JINGLE_ACTION_SESSION_INITIATE; |
| case ACTION_SESSION_INFO: |
| return JINGLE_ACTION_SESSION_INFO; |
| case ACTION_SESSION_ACCEPT: |
| return JINGLE_ACTION_SESSION_ACCEPT; |
| // Notice that reject and terminate both go to |
| // "session-terminate", but there is no "session-reject". |
| case ACTION_SESSION_REJECT: |
| case ACTION_SESSION_TERMINATE: |
| return JINGLE_ACTION_SESSION_TERMINATE; |
| case ACTION_TRANSPORT_INFO: |
| return JINGLE_ACTION_TRANSPORT_INFO; |
| case ACTION_TRANSPORT_ACCEPT: |
| return JINGLE_ACTION_TRANSPORT_ACCEPT; |
| default: |
| return ""; |
| } |
| } |
| |
| std::string ToGingleString(ActionType type) { |
| switch (type) { |
| case ACTION_SESSION_INITIATE: |
| return GINGLE_ACTION_INITIATE; |
| case ACTION_SESSION_INFO: |
| return GINGLE_ACTION_INFO; |
| case ACTION_SESSION_ACCEPT: |
| return GINGLE_ACTION_ACCEPT; |
| case ACTION_SESSION_REJECT: |
| return GINGLE_ACTION_REJECT; |
| case ACTION_SESSION_TERMINATE: |
| return GINGLE_ACTION_TERMINATE; |
| case ACTION_TRANSPORT_INFO: |
| return GINGLE_ACTION_CANDIDATES; |
| default: |
| return ""; |
| } |
| } |
| |
| |
| bool IsJingleMessage(const buzz::XmlElement* stanza) { |
| const buzz::XmlElement* jingle = stanza->FirstNamed(QN_JINGLE); |
| if (jingle == NULL) |
| return false; |
| |
| return (jingle->HasAttr(buzz::QN_ACTION) && jingle->HasAttr(QN_SID)); |
| } |
| |
| bool IsGingleMessage(const buzz::XmlElement* stanza) { |
| const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION); |
| if (session == NULL) |
| return false; |
| |
| return (session->HasAttr(buzz::QN_TYPE) && |
| session->HasAttr(buzz::QN_ID) && |
| session->HasAttr(QN_INITIATOR)); |
| } |
| |
| bool IsSessionMessage(const buzz::XmlElement* stanza) { |
| return (stanza->Name() == buzz::QN_IQ && |
| stanza->Attr(buzz::QN_TYPE) == buzz::STR_SET && |
| (IsJingleMessage(stanza) || |
| IsGingleMessage(stanza))); |
| } |
| |
| bool ParseGingleSessionMessage(const buzz::XmlElement* session, |
| SessionMessage* msg, |
| ParseError* error) { |
| msg->protocol = PROTOCOL_GINGLE; |
| std::string type_string = session->Attr(buzz::QN_TYPE); |
| msg->type = ToActionType(type_string); |
| msg->sid = session->Attr(buzz::QN_ID); |
| msg->initiator = session->Attr(QN_INITIATOR); |
| msg->action_elem = session; |
| |
| if (msg->type == ACTION_UNKNOWN) |
| return BadParse("unknown action: " + type_string, error); |
| |
| return true; |
| } |
| |
| bool ParseJingleSessionMessage(const buzz::XmlElement* jingle, |
| SessionMessage* msg, |
| ParseError* error) { |
| msg->protocol = PROTOCOL_JINGLE; |
| std::string type_string = jingle->Attr(buzz::QN_ACTION); |
| msg->type = ToActionType(type_string); |
| msg->sid = jingle->Attr(QN_SID); |
| msg->initiator = GetXmlAttr(jingle, QN_INITIATOR, buzz::STR_EMPTY); |
| msg->action_elem = jingle; |
| |
| if (msg->type == ACTION_UNKNOWN) |
| return BadParse("unknown action: " + type_string, error); |
| |
| return true; |
| } |
| |
| bool ParseHybridSessionMessage(const buzz::XmlElement* jingle, |
| SessionMessage* msg, |
| ParseError* error) { |
| if (!ParseJingleSessionMessage(jingle, msg, error)) |
| return false; |
| msg->protocol = PROTOCOL_HYBRID; |
| |
| return true; |
| } |
| |
| bool ParseSessionMessage(const buzz::XmlElement* stanza, |
| SessionMessage* msg, |
| ParseError* error) { |
| msg->id = stanza->Attr(buzz::QN_ID); |
| msg->from = stanza->Attr(buzz::QN_FROM); |
| msg->to = stanza->Attr(buzz::QN_TO); |
| msg->stanza = stanza; |
| |
| const buzz::XmlElement* jingle = stanza->FirstNamed(QN_JINGLE); |
| const buzz::XmlElement* session = stanza->FirstNamed(QN_GINGLE_SESSION); |
| if (jingle && session) |
| return ParseHybridSessionMessage(jingle, msg, error); |
| if (jingle != NULL) |
| return ParseJingleSessionMessage(jingle, msg, error); |
| if (session != NULL) |
| return ParseGingleSessionMessage(session, msg, error); |
| return false; |
| } |
| |
| buzz::XmlElement* WriteGingleAction(const SessionMessage& msg, |
| const XmlElements& action_elems) { |
| buzz::XmlElement* session = new buzz::XmlElement(QN_GINGLE_SESSION, true); |
| session->AddAttr(buzz::QN_TYPE, ToGingleString(msg.type)); |
| session->AddAttr(buzz::QN_ID, msg.sid); |
| session->AddAttr(QN_INITIATOR, msg.initiator); |
| AddXmlChildren(session, action_elems); |
| return session; |
| } |
| |
| buzz::XmlElement* WriteJingleAction(const SessionMessage& msg, |
| const XmlElements& action_elems) { |
| buzz::XmlElement* jingle = new buzz::XmlElement(QN_JINGLE, true); |
| jingle->AddAttr(buzz::QN_ACTION, ToJingleString(msg.type)); |
| jingle->AddAttr(QN_SID, msg.sid); |
| if (msg.type == ACTION_SESSION_INITIATE) { |
| jingle->AddAttr(QN_INITIATOR, msg.initiator); |
| } |
| AddXmlChildren(jingle, action_elems); |
| return jingle; |
| } |
| |
| void WriteSessionMessage(const SessionMessage& msg, |
| const XmlElements& action_elems, |
| buzz::XmlElement* stanza) { |
| stanza->SetAttr(buzz::QN_TO, msg.to); |
| stanza->SetAttr(buzz::QN_TYPE, buzz::STR_SET); |
| |
| if (msg.protocol == PROTOCOL_GINGLE) { |
| stanza->AddElement(WriteGingleAction(msg, action_elems)); |
| } else { |
| stanza->AddElement(WriteJingleAction(msg, action_elems)); |
| } |
| } |
| |
| |
| TransportParser* GetTransportParser(const TransportParserMap& trans_parsers, |
| const std::string& name) { |
| TransportParserMap::const_iterator map = trans_parsers.find(name); |
| if (map == trans_parsers.end()) { |
| return NULL; |
| } else { |
| return map->second; |
| } |
| } |
| |
| bool ParseCandidates(SignalingProtocol protocol, |
| const buzz::XmlElement* candidates_elem, |
| const TransportParserMap& trans_parsers, |
| const std::string& transport_type, |
| Candidates* candidates, |
| ParseError* error) { |
| TransportParser* trans_parser = |
| GetTransportParser(trans_parsers, transport_type); |
| if (trans_parser == NULL) |
| return BadParse("unknown transport type: " + transport_type, error); |
| |
| return trans_parser->ParseCandidates(protocol, candidates_elem, |
| candidates, error); |
| } |
| |
| bool ParseGingleTransportInfos(const buzz::XmlElement* action_elem, |
| const ContentInfos& contents, |
| const TransportParserMap& trans_parsers, |
| TransportInfos* tinfos, |
| ParseError* error) { |
| TransportInfo tinfo(CN_OTHER, NS_GINGLE_P2P, Candidates()); |
| if (!ParseCandidates(PROTOCOL_GINGLE, action_elem, |
| trans_parsers, NS_GINGLE_P2P, |
| &tinfo.candidates, error)) |
| return false; |
| |
| bool has_audio = FindContentInfoByName(contents, CN_AUDIO) != NULL; |
| bool has_video = FindContentInfoByName(contents, CN_VIDEO) != NULL; |
| |
| // If we don't have media, no need to separate the candidates. |
| if (!has_audio && !has_video) { |
| tinfos->push_back(tinfo); |
| return true; |
| } |
| |
| // If we have media, separate the candidates. Create the |
| // TransportInfo here to avoid copying the candidates. |
| TransportInfo audio_tinfo(CN_AUDIO, NS_GINGLE_P2P, Candidates()); |
| TransportInfo video_tinfo(CN_VIDEO, NS_GINGLE_P2P, Candidates()); |
| for (Candidates::iterator cand = tinfo.candidates.begin(); |
| cand != tinfo.candidates.end(); cand++) { |
| if (cand->name() == GINGLE_CANDIDATE_NAME_RTP || |
| cand->name() == GINGLE_CANDIDATE_NAME_RTCP) { |
| audio_tinfo.candidates.push_back(*cand); |
| } else if (cand->name() == GINGLE_CANDIDATE_NAME_VIDEO_RTP || |
| cand->name() == GINGLE_CANDIDATE_NAME_VIDEO_RTCP) { |
| video_tinfo.candidates.push_back(*cand); |
| } |
| } |
| |
| if (has_audio) { |
| tinfos->push_back(audio_tinfo); |
| } |
| |
| if (has_video) { |
| tinfos->push_back(video_tinfo); |
| } |
| |
| return true; |
| } |
| |
| bool ParseJingleTransportInfo(const buzz::XmlElement* trans_elem, |
| const ContentInfo& content, |
| const TransportParserMap& trans_parsers, |
| TransportInfos* tinfos, |
| ParseError* error) { |
| std::string transport_type = trans_elem->Name().Namespace(); |
| TransportInfo tinfo(content.name, transport_type, Candidates()); |
| if (!ParseCandidates(PROTOCOL_JINGLE, trans_elem, |
| trans_parsers, transport_type, |
| &tinfo.candidates, error)) |
| return false; |
| |
| tinfos->push_back(tinfo); |
| return true; |
| } |
| |
| bool ParseJingleTransportInfos(const buzz::XmlElement* jingle, |
| const ContentInfos& contents, |
| const TransportParserMap trans_parsers, |
| TransportInfos* tinfos, |
| ParseError* error) { |
| for (const buzz::XmlElement* pair_elem |
| = jingle->FirstNamed(QN_JINGLE_CONTENT); |
| pair_elem != NULL; |
| pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) { |
| std::string content_name; |
| if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME, |
| &content_name, error)) |
| return false; |
| |
| const ContentInfo* content = FindContentInfoByName(contents, content_name); |
| if (!content) |
| return BadParse("Unknown content name: " + content_name, error); |
| |
| const buzz::XmlElement* trans_elem; |
| if (!RequireXmlChild(pair_elem, LN_TRANSPORT, &trans_elem, error)) |
| return false; |
| |
| if (!ParseJingleTransportInfo(trans_elem, *content, trans_parsers, |
| tinfos, error)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| buzz::XmlElement* NewTransportElement(const std::string& name) { |
| return new buzz::XmlElement(buzz::QName(name, LN_TRANSPORT), true); |
| } |
| |
| bool WriteCandidates(SignalingProtocol protocol, |
| const std::string& trans_type, |
| const Candidates& candidates, |
| const TransportParserMap& trans_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| TransportParser* trans_parser = GetTransportParser(trans_parsers, trans_type); |
| if (trans_parser == NULL) |
| return BadWrite("unknown transport type: " + trans_type, error); |
| |
| return trans_parser->WriteCandidates(protocol, candidates, elems, error); |
| } |
| |
| bool WriteGingleTransportInfos(const TransportInfos& tinfos, |
| const TransportParserMap& trans_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| for (TransportInfos::const_iterator tinfo = tinfos.begin(); |
| tinfo != tinfos.end(); ++tinfo) { |
| if (!WriteCandidates(PROTOCOL_GINGLE, |
| tinfo->transport_type, tinfo->candidates, |
| trans_parsers, elems, error)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool WriteJingleTransportInfo(const TransportInfo& tinfo, |
| const TransportParserMap& trans_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| XmlElements candidate_elems; |
| if (!WriteCandidates(PROTOCOL_JINGLE, |
| tinfo.transport_type, tinfo.candidates, trans_parsers, |
| &candidate_elems, error)) |
| return false; |
| |
| buzz::XmlElement* trans_elem = NewTransportElement(tinfo.transport_type); |
| AddXmlChildren(trans_elem, candidate_elems); |
| elems->push_back(trans_elem); |
| return true; |
| } |
| |
| void WriteJingleContentPair(const std::string name, |
| const XmlElements& pair_elems, |
| XmlElements* elems) { |
| buzz::XmlElement* pair_elem = new buzz::XmlElement(QN_JINGLE_CONTENT); |
| pair_elem->SetAttr(QN_JINGLE_CONTENT_NAME, name); |
| pair_elem->SetAttr(QN_CREATOR, LN_INITIATOR); |
| AddXmlChildren(pair_elem, pair_elems); |
| |
| elems->push_back(pair_elem); |
| } |
| |
| bool WriteJingleTransportInfos(const TransportInfos& tinfos, |
| const TransportParserMap& trans_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| for (TransportInfos::const_iterator tinfo = tinfos.begin(); |
| tinfo != tinfos.end(); ++tinfo) { |
| XmlElements pair_elems; |
| if (!WriteJingleTransportInfo(*tinfo, trans_parsers, |
| &pair_elems, error)) |
| return false; |
| |
| WriteJingleContentPair(tinfo->content_name, pair_elems, elems); |
| } |
| |
| return true; |
| } |
| |
| ContentParser* GetContentParser(const ContentParserMap& content_parsers, |
| const std::string& type) { |
| ContentParserMap::const_iterator map = content_parsers.find(type); |
| if (map == content_parsers.end()) { |
| return NULL; |
| } else { |
| return map->second; |
| } |
| } |
| |
| bool ParseContentInfo(SignalingProtocol protocol, |
| const std::string& name, |
| const std::string& type, |
| const buzz::XmlElement* elem, |
| const ContentParserMap& parsers, |
| ContentInfos* contents, |
| ParseError* error) { |
| ContentParser* parser = GetContentParser(parsers, type); |
| if (parser == NULL) |
| return BadParse("unknown application content: " + type, error); |
| |
| const ContentDescription* desc; |
| if (!parser->ParseContent(protocol, elem, &desc, error)) |
| return false; |
| |
| contents->push_back(ContentInfo(name, type, desc)); |
| return true; |
| } |
| |
| bool ParseContentType(const buzz::XmlElement* parent_elem, |
| std::string* content_type, |
| const buzz::XmlElement** content_elem, |
| ParseError* error) { |
| if (!RequireXmlChild(parent_elem, LN_DESCRIPTION, content_elem, error)) |
| return false; |
| |
| *content_type = (*content_elem)->Name().Namespace(); |
| return true; |
| } |
| |
| bool ParseGingleContentInfos(const buzz::XmlElement* session, |
| const ContentParserMap& content_parsers, |
| ContentInfos* contents, |
| ParseError* error) { |
| std::string content_type; |
| const buzz::XmlElement* content_elem; |
| if (!ParseContentType(session, &content_type, &content_elem, error)) |
| return false; |
| |
| if (content_type == NS_GINGLE_VIDEO) { |
| // A parser parsing audio or video content should look at the |
| // namespace and only parse the codecs relevant to that namespace. |
| // We use this to control which codecs get parsed: first audio, |
| // then video. |
| talk_base::scoped_ptr<buzz::XmlElement> audio_elem( |
| new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT)); |
| CopyXmlChildren(content_elem, audio_elem.get()); |
| if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP, |
| audio_elem.get(), content_parsers, |
| contents, error)) |
| return false; |
| |
| if (!ParseContentInfo(PROTOCOL_GINGLE, CN_VIDEO, NS_JINGLE_RTP, |
| content_elem, content_parsers, |
| contents, error)) |
| return false; |
| } else if (content_type == NS_GINGLE_AUDIO) { |
| if (!ParseContentInfo(PROTOCOL_GINGLE, CN_AUDIO, NS_JINGLE_RTP, |
| content_elem, content_parsers, |
| contents, error)) |
| return false; |
| } else { |
| if (!ParseContentInfo(PROTOCOL_GINGLE, CN_OTHER, content_type, |
| content_elem, content_parsers, |
| contents, error)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool ParseJingleContentInfos(const buzz::XmlElement* jingle, |
| const ContentParserMap& content_parsers, |
| ContentInfos* contents, |
| ParseError* error) { |
| for (const buzz::XmlElement* pair_elem |
| = jingle->FirstNamed(QN_JINGLE_CONTENT); |
| pair_elem != NULL; |
| pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) { |
| std::string content_name; |
| if (!RequireXmlAttr(pair_elem, QN_JINGLE_CONTENT_NAME, |
| &content_name, error)) |
| return false; |
| |
| std::string content_type; |
| const buzz::XmlElement* content_elem; |
| if (!ParseContentType(pair_elem, &content_type, &content_elem, error)) |
| return false; |
| |
| if (!ParseContentInfo(PROTOCOL_JINGLE, content_name, content_type, |
| content_elem, content_parsers, |
| contents, error)) |
| return false; |
| } |
| return true; |
| } |
| |
| buzz::XmlElement* WriteContentInfo(SignalingProtocol protocol, |
| const ContentInfo& content, |
| const ContentParserMap& parsers, |
| WriteError* error) { |
| ContentParser* parser = GetContentParser(parsers, content.type); |
| if (parser == NULL) { |
| BadWrite("unknown content type: " + content.type, error); |
| return NULL; |
| } |
| |
| buzz::XmlElement* elem = NULL; |
| if (!parser->WriteContent(protocol, content.description, &elem, error)) |
| return NULL; |
| |
| return elem; |
| } |
| |
| bool WriteGingleContentInfos(const ContentInfos& contents, |
| const ContentParserMap& parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| if (contents.size() == 1) { |
| buzz::XmlElement* elem = WriteContentInfo( |
| PROTOCOL_GINGLE, contents.front(), parsers, error); |
| if (!elem) |
| return false; |
| |
| elems->push_back(elem); |
| } else if (contents.size() == 2 && |
| contents.at(0).type == NS_JINGLE_RTP && |
| contents.at(1).type == NS_JINGLE_RTP) { |
| // Special-case audio + video contents so that they are "merged" |
| // into one "video" content. |
| buzz::XmlElement* audio = WriteContentInfo( |
| PROTOCOL_GINGLE, contents.at(0), parsers, error); |
| if (!audio) |
| return false; |
| |
| buzz::XmlElement* video = WriteContentInfo( |
| PROTOCOL_GINGLE, contents.at(1), parsers, error); |
| if (!video) { |
| delete audio; |
| return false; |
| } |
| |
| CopyXmlChildren(audio, video); |
| elems->push_back(video); |
| delete audio; |
| } else { |
| return BadWrite("Gingle protocol may only have one content.", error); |
| } |
| |
| return true; |
| } |
| |
| const TransportInfo* GetTransportInfoByContentName( |
| const TransportInfos& tinfos, const std::string& content_name) { |
| for (TransportInfos::const_iterator tinfo = tinfos.begin(); |
| tinfo != tinfos.end(); ++tinfo) { |
| if (content_name == tinfo->content_name) { |
| return &*tinfo; |
| } |
| } |
| return NULL; |
| } |
| |
| bool WriteJingleContentPairs(const ContentInfos& contents, |
| const ContentParserMap& content_parsers, |
| const TransportInfos& tinfos, |
| const TransportParserMap& trans_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| for (ContentInfos::const_iterator content = contents.begin(); |
| content != contents.end(); ++content) { |
| const TransportInfo* tinfo = |
| GetTransportInfoByContentName(tinfos, content->name); |
| if (!tinfo) |
| return BadWrite("No transport for content: " + content->name, error); |
| |
| XmlElements pair_elems; |
| buzz::XmlElement* elem = WriteContentInfo( |
| PROTOCOL_JINGLE, *content, content_parsers, error); |
| if (!elem) |
| return false; |
| pair_elems.push_back(elem); |
| |
| if (!WriteJingleTransportInfo(*tinfo, trans_parsers, |
| &pair_elems, error)) |
| return false; |
| |
| WriteJingleContentPair(content->name, pair_elems, elems); |
| } |
| return true; |
| } |
| |
| bool ParseContentType(SignalingProtocol protocol, |
| const buzz::XmlElement* action_elem, |
| std::string* content_type, |
| ParseError* error) { |
| const buzz::XmlElement* content_elem; |
| if (protocol == PROTOCOL_GINGLE) { |
| if (!ParseContentType(action_elem, content_type, &content_elem, error)) |
| return false; |
| |
| // Internally, we only use NS_JINGLE_RTP. |
| if (*content_type == NS_GINGLE_AUDIO || |
| *content_type == NS_GINGLE_VIDEO) |
| *content_type = NS_JINGLE_RTP; |
| } else { |
| const buzz::XmlElement* pair_elem |
| = action_elem->FirstNamed(QN_JINGLE_CONTENT); |
| if (pair_elem == NULL) |
| return BadParse("No contents found", error); |
| |
| if (!ParseContentType(pair_elem, content_type, &content_elem, error)) |
| return false; |
| |
| // If there is more than one content type, return an error. |
| for (; pair_elem != NULL; |
| pair_elem = pair_elem->NextNamed(QN_JINGLE_CONTENT)) { |
| std::string content_type2; |
| if (!ParseContentType(pair_elem, &content_type2, &content_elem, error)) |
| return false; |
| |
| if (content_type2 != *content_type) |
| return BadParse("More than one content type found", error); |
| } |
| } |
| |
| return true; |
| } |
| |
| static bool ParseContentMessage( |
| SignalingProtocol protocol, |
| const buzz::XmlElement* action_elem, |
| bool expect_transports, |
| const ContentParserMap& content_parsers, |
| const TransportParserMap& trans_parsers, |
| SessionInitiate* init, |
| ParseError* error) { |
| init->owns_contents = true; |
| if (protocol == PROTOCOL_GINGLE) { |
| if (!ParseGingleContentInfos(action_elem, content_parsers, |
| &init->contents, error)) |
| return false; |
| |
| if (expect_transports && |
| !ParseGingleTransportInfos(action_elem, init->contents, trans_parsers, |
| &init->transports, error)) |
| return false; |
| } else { |
| if (!ParseJingleContentInfos(action_elem, content_parsers, |
| &init->contents, error)) |
| return false; |
| |
| if (expect_transports && |
| !ParseJingleTransportInfos(action_elem, init->contents, trans_parsers, |
| &init->transports, error)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool WriteContentMessage( |
| SignalingProtocol protocol, |
| const ContentInfos& contents, |
| const TransportInfos& tinfos, |
| const ContentParserMap& content_parsers, |
| const TransportParserMap& transport_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| if (protocol == PROTOCOL_GINGLE) { |
| if (!WriteGingleContentInfos(contents, content_parsers, elems, error)) |
| return false; |
| |
| if (!WriteGingleTransportInfos(tinfos, transport_parsers, |
| elems, error)) |
| return false; |
| } else { |
| if (!WriteJingleContentPairs(contents, content_parsers, |
| tinfos, transport_parsers, |
| elems, error)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ParseSessionInitiate(SignalingProtocol protocol, |
| const buzz::XmlElement* action_elem, |
| const ContentParserMap& content_parsers, |
| const TransportParserMap& trans_parsers, |
| SessionInitiate* init, |
| ParseError* error) { |
| bool expect_transports = true; |
| return ParseContentMessage(protocol, action_elem, expect_transports, |
| content_parsers, trans_parsers, |
| init, error); |
| } |
| |
| |
| bool WriteSessionInitiate(SignalingProtocol protocol, |
| const ContentInfos& contents, |
| const TransportInfos& tinfos, |
| const ContentParserMap& content_parsers, |
| const TransportParserMap& transport_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| return WriteContentMessage(protocol, contents, tinfos, |
| content_parsers, transport_parsers, |
| elems, error); |
| } |
| |
| bool ParseSessionAccept(SignalingProtocol protocol, |
| const buzz::XmlElement* action_elem, |
| const ContentParserMap& content_parsers, |
| const TransportParserMap& transport_parsers, |
| SessionAccept* accept, |
| ParseError* error) { |
| bool expect_transports = true; |
| return ParseContentMessage(protocol, action_elem, expect_transports, |
| content_parsers, transport_parsers, |
| accept, error); |
| } |
| |
| bool WriteSessionAccept(SignalingProtocol protocol, |
| const ContentInfos& contents, |
| const TransportInfos& tinfos, |
| const ContentParserMap& content_parsers, |
| const TransportParserMap& transport_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| return WriteContentMessage(protocol, contents, tinfos, |
| content_parsers, transport_parsers, |
| elems, error); |
| } |
| |
| bool ParseSessionTerminate(SignalingProtocol protocol, |
| const buzz::XmlElement* action_elem, |
| SessionTerminate* term, |
| ParseError* error) { |
| if (protocol == PROTOCOL_GINGLE) { |
| const buzz::XmlElement* reason_elem = action_elem->FirstElement(); |
| if (reason_elem != NULL) { |
| term->reason = reason_elem->Name().LocalPart(); |
| const buzz::XmlElement *debug_elem = reason_elem->FirstElement(); |
| if (debug_elem != NULL) { |
| term->debug_reason = debug_elem->Name().LocalPart(); |
| } |
| } |
| return true; |
| } else { |
| const buzz::XmlElement* reason_elem = |
| action_elem->FirstNamed(QN_JINGLE_REASON); |
| if (reason_elem) { |
| reason_elem = reason_elem->FirstElement(); |
| if (reason_elem) { |
| term->reason = reason_elem->Name().LocalPart(); |
| } |
| } |
| return true; |
| } |
| } |
| |
| void WriteSessionTerminate(SignalingProtocol protocol, |
| const SessionTerminate& term, |
| XmlElements* elems) { |
| if (protocol == PROTOCOL_GINGLE) { |
| elems->push_back(new buzz::XmlElement(buzz::QName(NS_GINGLE, term.reason))); |
| } else { |
| if (!term.reason.empty()) { |
| buzz::XmlElement* reason_elem = new buzz::XmlElement(QN_JINGLE_REASON); |
| reason_elem->AddElement(new buzz::XmlElement( |
| buzz::QName(NS_JINGLE, term.reason))); |
| elems->push_back(reason_elem); |
| } |
| } |
| } |
| |
| bool ParseDescriptionInfo(SignalingProtocol protocol, |
| const buzz::XmlElement* action_elem, |
| const ContentParserMap& content_parsers, |
| const TransportParserMap& transport_parsers, |
| DescriptionInfo* description_info, |
| ParseError* error) { |
| bool expect_transports = false; |
| return ParseContentMessage(protocol, action_elem, expect_transports, |
| content_parsers, transport_parsers, |
| description_info, error); |
| } |
| |
| bool ParseTransportInfos(SignalingProtocol protocol, |
| const buzz::XmlElement* action_elem, |
| const ContentInfos& contents, |
| const TransportParserMap& trans_parsers, |
| TransportInfos* tinfos, |
| ParseError* error) { |
| if (protocol == PROTOCOL_GINGLE) { |
| return ParseGingleTransportInfos( |
| action_elem, contents, trans_parsers, tinfos, error); |
| } else { |
| return ParseJingleTransportInfos( |
| action_elem, contents, trans_parsers, tinfos, error); |
| } |
| } |
| |
| bool WriteTransportInfos(SignalingProtocol protocol, |
| const TransportInfos& tinfos, |
| const TransportParserMap& trans_parsers, |
| XmlElements* elems, |
| WriteError* error) { |
| if (protocol == PROTOCOL_GINGLE) { |
| return WriteGingleTransportInfos(tinfos, trans_parsers, |
| elems, error); |
| } else { |
| return WriteJingleTransportInfos(tinfos, trans_parsers, |
| elems, error); |
| } |
| } |
| |
| bool GetUriTarget(const std::string& prefix, const std::string& str, |
| std::string* after) { |
| size_t pos = str.find(prefix); |
| if (pos == std::string::npos) |
| return false; |
| |
| *after = str.substr(pos + prefix.size(), std::string::npos); |
| return true; |
| } |
| |
| bool FindSessionRedirect(const buzz::XmlElement* stanza, |
| SessionRedirect* redirect) { |
| const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR); |
| if (error_elem == NULL) |
| return false; |
| |
| const buzz::XmlElement* redirect_elem = |
| error_elem->FirstNamed(QN_GINGLE_REDIRECT); |
| if (redirect_elem == NULL) |
| redirect_elem = error_elem->FirstNamed(buzz::QN_STANZA_REDIRECT); |
| if (redirect_elem == NULL) |
| return false; |
| |
| if (!GetUriTarget(STR_REDIRECT_PREFIX, redirect_elem->BodyText(), |
| &redirect->target)) |
| return false; |
| |
| return true; |
| } |
| |
| } // namespace cricket |