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