reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
SdpContents.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5#include <algorithm>
6
10#include "rutil/ParseBuffer.hxx"
11#include "rutil/DataStream.hxx"
13#include "rutil/Logger.hxx"
15
16#define RESIPROCATE_SUBSYSTEM resip::Subsystem::SDP
17
18using namespace resip;
19using namespace std;
20
25
26const std::map<Data, std::reference_wrapper<const SdpContents::Session::Direction>> SdpContents::Session::Direction::directions = {
31 };
32
34
35bool
37{
38 static ContentsFactory<SdpContents> factory;
39 (void)factory;
40 return true;
41}
42
43const char* NetworkType[] = {"???", "IP4", "IP6"};
44
45static const Data rtpmap("rtpmap");
46static const Data fmtp("fmtp");
47
48// RFC2327 6. page 9
49// "parsers should be tolerant and accept records terminated with a single
50// newline character"
51void
53{
54 while(!pb.eof() && (*pb.position() == Symbols::SPACE[0] ||
55 *pb.position() == Symbols::TAB[0]))
56 {
57 pb.skipChar();
58 }
59
60 if (*pb.position() == Symbols::LF[0])
61 {
62 pb.skipChar();
63 }
64 else
65 {
66 // allow extra 0x0d bytes.
67 while(*pb.position() == Symbols::CR[0])
68 {
69 pb.skipChar();
70 }
71 pb.skipChar(Symbols::LF[0]);
72 }
73
74}
75
77 : mAttributeList(rhs.mAttributeList),
78 mAttributes(rhs.mAttributes)
79{
80}
81
85
88{
89 if (this != &rhs)
90 {
91 mAttributeList = rhs.mAttributeList;
92 mAttributes = rhs.mAttributes;
93 }
94 return *this;
95}
96
97bool
99{
100 return mAttributes.find(key) != mAttributes.end();
101}
102
103const list<Data>&
105{
106 if (!exists(key))
107 {
108 static const list<Data> emptyList;
109 return emptyList;
110 }
111 return mAttributes.find(key)->second;
112}
113
115AttributeHelper::encode(EncodeStream& s) const
116{
117 for (std::list<std::pair<Data, Data> >::const_iterator i = mAttributeList.begin();
118 i != mAttributeList.end(); ++i)
119 {
120 s << "a=" << i->first;
121 if (!i->second.empty())
122 {
123 s << Symbols::COLON[0] << i->second;
124 }
125 s << Symbols::CRLF;
126 }
127 return s;
128}
129
130void
132{
133 while (!pb.eof() && *pb.position() == 'a')
134 {
135 Data key;
136 Data value;
137
138 pb.skipChar('a');
139 const char* anchor = pb.skipChar(Symbols::EQUALS[0]);
141 pb.data(key, anchor);
142 if (!pb.eof() && *pb.position() == Symbols::COLON[0])
143 {
144 anchor = pb.skipChar(Symbols::COLON[0]);
146 pb.data(value, anchor);
147 }
148
149 if(!pb.eof()) skipEol(pb);
150
151 // Repeated names (eg. candidate) share one entry, so this only limits junk
152 if (mAttributes.size() >= MaxParsedAttributeNames && !exists(key))
153 {
154 pb.fail(__FILE__, __LINE__, "Too many distinct SDP attribute names");
155 }
156
157 mAttributeList.push_back(std::make_pair(key, value));
158 mAttributes[key].push_back(value);
159 }
160}
161
162void
163AttributeHelper::addAttribute(const Data& key, const Data& value)
164{
165 mAttributeList.push_back(std::make_pair(key, value));
166 mAttributes[key].push_back(value);
167}
168
169void
171{
172 for (std::list<std::pair<Data, Data> >::iterator i = mAttributeList.begin();
173 i != mAttributeList.end(); )
174 {
175 std::list<std::pair<Data, Data> >::iterator j = i++;
176 if (j->first == key)
177 {
178 mAttributeList.erase(j);
179 }
180 }
181 mAttributes.erase(key);
182}
183
185{
186}
187
188SdpContents::SdpContents(const HeaderFieldValue& hfv, const Mime& contentTypes)
189 : Contents(hfv, contentTypes)
190{
191}
192
193//SdpContents::SdpContents(const SdpContents& rhs)
194// : Contents(rhs),
195// mSession(rhs.mSession)
196//{
197//}
198
202
203
206{
207 if (this != &rhs)
208 {
210 mSession = rhs.mSession;
211 }
212 return *this;
213}
214
217{
218 return new SdpContents(*this);
219}
220
221void
223{
224 mSession.parse(pb);
225}
226
228SdpContents::encodeParsed(EncodeStream& s) const
229{
230 mSession.encode(s);
231 return s;
232}
233
234const Mime&
236{
237 static Mime type("application", "sdp");
238 return type;
239}
240
241static Data nullOrigin("0.0.0.0");
242
243SdpContents::Session::Origin::Origin()
244 : mUser(),
245 mSessionId(0),
246 mVersion(0),
247 mAddrType(IP4),
248 mAddress(nullOrigin)
249{}
250
251SdpContents::Session::Origin::Origin(const Origin& rhs)
252 : mUser(rhs.mUser),
253 mSessionId(rhs.mSessionId),
254 mVersion(rhs.mVersion),
255 mAddrType(rhs.mAddrType),
256 mAddress(rhs.mAddress)
257{
258}
259
262{
263 if (this != &rhs)
264 {
265 mUser = rhs.mUser;
266 mSessionId = rhs.mSessionId;
267 mVersion = rhs.mVersion;
268 mAddrType = rhs.mAddrType;
269 mAddress = rhs.mAddress;
270 }
271 return *this;
272}
273
274
275SdpContents::Session::Origin::Origin(const Data& user,
276 const uint64_t& sessionId,
277 const uint64_t& version,
278 AddrType addr,
279 const Data& address)
280 : mUser(user),
281 mSessionId(sessionId),
282 mVersion(version),
283 mAddrType(addr),
284 mAddress(address)
285{}
286
289{
290 s << "o="
291 << mUser << Symbols::SPACE[0]
292 << mSessionId << Symbols::SPACE[0]
293 << mVersion << Symbols::SPACE[0]
294 << "IN "
295 << NetworkType[mAddrType] << Symbols::SPACE[0]
296 << mAddress << Symbols::CRLF;
297 return s;
298}
299
300void
302{
303 mAddress = host;
304 mAddrType = addr;
305}
306
307void
309{
310 pb.skipChar('o');
311 const char* anchor = pb.skipChar(Symbols::EQUALS[0]);
312
314 pb.data(mUser, anchor);
315
316 anchor = pb.skipChar(Symbols::SPACE[0]);
317 try
318 {
319 mSessionId = pb.uInt64();
320 }
321 catch(ParseException& e)
322 {
323 WarningLog(<< "Exception parsing origin sessionid: " << e);
324 }
326
327 anchor = pb.skipChar(Symbols::SPACE[0]);
328 try
329 {
330 mVersion = pb.uInt64();
331 }
332 catch(ParseException& e)
333 {
334 WarningLog(<< "Exception parsing origin version: " << e);
335 }
337
339 pb.skipChar('I');
340 pb.skipChar('N');
341
342 anchor = pb.skipChar(Symbols::SPACE[0]);
344 Data addrType;
345 pb.data(addrType, anchor);
346 if (addrType == NetworkType[IP4])
347 {
348 mAddrType = IP4;
349 }
350 else if (addrType == NetworkType[IP6])
351 {
352 mAddrType = IP6;
353 }
354 else
355 {
356 mAddrType = static_cast<AddrType>(0);
357 }
358
359 anchor = pb.skipChar(Symbols::SPACE[0]);
361 pb.data(mAddress, anchor);
362
363 skipEol(pb);
364}
365
366SdpContents::Session::Email::Email(const Data& address,
367 const Data& freeText)
368 : mAddress(address),
369 mFreeText(freeText)
370{}
371
372SdpContents::Session::Email::Email(const Email& rhs)
373 : mAddress(rhs.mAddress),
374 mFreeText(rhs.mFreeText)
375{}
376
379{
380 if (this != &rhs)
381 {
382 mAddress = rhs.mAddress;
383 mFreeText = rhs.mFreeText;
384 }
385 return *this;
386}
387
390{
391 s << "e=" << mAddress;
392 if (!mFreeText.empty())
393 {
394 s << Symbols::SPACE[0];
395 s << Symbols::LPAREN[0] << mFreeText << Symbols::RPAREN[0];
396 }
397 s << Symbols::CRLF;
398
399 return s;
400}
401
402// helper to parse email and phone numbers with display name
403void parseEorP(ParseBuffer& pb, Data& eOrp, Data& freeText)
404{
405 // =mjh@isi.edu (Mark Handley)
406 // =mjh@isi.edu
407 // =Mark Handley <mjh@isi.edu>
408 // =<mjh@isi.edu>
409
410 const char* anchor = pb.skipChar(Symbols::EQUALS[0]);
411
412 pb.skipToOneOf("<(\n\r"); // find a left angle bracket "<", a left paren "(", or a CR
413 switch (*pb.position())
414 {
415 case '\n': // Symbols::CR[0]
416 case '\r': // Symbols::LF[0]
417 // mjh@isi.edu
418 // ^
419 pb.data(eOrp, anchor);
420 break;
421
422 case '<': // Symbols::LA_QUOTE[0]
423 // Mark Handley <mjh@isi.edu>
424 // ^
425 // <mjh@isi.edu>
426 // ^
427
428 pb.data(freeText, anchor);
429 anchor = pb.skipChar();
431 pb.data(eOrp, anchor);
433 break;
434
435 case '(': // Symbols::LPAREN[0]
436 // mjh@isi.edu (Mark Handley)
437 // ^
438
439 pb.data(eOrp, anchor);
440 anchor = pb.skipChar();
442 pb.data(freeText, anchor);
444 break;
445 default:
446 resip_assert(0);
447 }
448}
449
450void
452{
453 pb.skipChar('e');
454 parseEorP(pb, mAddress, mFreeText);
455 skipEol(pb);
456}
457
458SdpContents::Session::Phone::Phone(const Data& number,
459 const Data& freeText)
460 : mNumber(number),
461 mFreeText(freeText)
462{}
463
464SdpContents::Session::Phone::Phone(const Phone& rhs)
465 : mNumber(rhs.mNumber),
466 mFreeText(rhs.mFreeText)
467{}
468
471{
472 if (this != &rhs)
473 {
474 mNumber = rhs.mNumber;
475 mFreeText = rhs.mFreeText;
476 }
477 return *this;
478}
479
482{
483 s << "p=" << mNumber;
484 if (!mFreeText.empty())
485 {
486 s << Symbols::SPACE[0];
487 s << Symbols::LPAREN[0] << mFreeText << Symbols::RPAREN[0];
488 }
489 s << Symbols::CRLF;
490
491 return s;
492}
493
494void
496{
497 pb.skipChar('p');
498 parseEorP(pb, mNumber, mFreeText);
499 skipEol(pb);
500}
501
502SdpContents::Session::Connection::Connection(AddrType addType,
503 const Data& address,
504 unsigned long ttl)
505 : mAddrType(addType),
506 mAddress(address),
507 mTTL(ttl)
508{}
509
510SdpContents::Session::Connection::Connection()
511 : mAddrType(IP4),
512 mAddress(),
513 mTTL(0)
514{}
515
516SdpContents::Session::Connection::Connection(const Connection& rhs)
517 : mAddrType(rhs.mAddrType),
518 mAddress(rhs.mAddress),
519 mTTL(rhs.mTTL)
520{
521}
522
525{
526 if (this != &rhs)
527 {
528 mAddrType = rhs.mAddrType;
529 mAddress = rhs.mAddress;
530 mTTL = rhs.mTTL;
531 }
532 return *this;
533}
534
537{
538 s << "c=IN "
539 << NetworkType[mAddrType] << Symbols::SPACE[0] << mAddress;
540
541 if (mTTL)
542 {
543 s << Symbols::SLASH[0] << mTTL;
544 }
545 s << Symbols::CRLF;
546 return s;
547}
548
549void
551{
552 mAddress = host;
553 mAddrType = addr;
554}
555
556void
558{
559 pb.skipChar('c');
561 pb.skipChar('I');
562 pb.skipChar('N');
563
564 const char* anchor = pb.skipChar(Symbols::SPACE[0]);
566 Data addrType;
567 pb.data(addrType, anchor);
568 if (addrType == NetworkType[IP4])
569 {
570 mAddrType = IP4;
571 }
572 else if (addrType == NetworkType[IP6])
573 {
574 mAddrType = IP6;
575 }
576 else
577 {
578 mAddrType = static_cast<AddrType>(0);
579 }
580
581 anchor = pb.skipChar();
583 pb.data(mAddress, anchor);
584
585 mTTL = 0;
586 if (mAddrType == IP4 && !pb.eof() && *pb.position() == Symbols::SLASH[0])
587 {
588 pb.skipChar();
589 mTTL = pb.integer();
590 }
591
592 // multicast dealt with above this parser
593 if (!pb.eof() && *pb.position() != Symbols::SLASH[0])
594 {
595 skipEol(pb);
596 }
597}
598
599SdpContents::Session::Bandwidth::Bandwidth(const Data& modifier,
600 unsigned long kbPerSecond)
601 : mModifier(modifier),
602 mKbPerSecond(kbPerSecond)
603{}
604
605SdpContents::Session::Bandwidth::Bandwidth(const Bandwidth& rhs)
606 : mModifier(rhs.mModifier),
607 mKbPerSecond(rhs.mKbPerSecond)
608{}
609
612{
613 if (this != &rhs)
614 {
615 mModifier = rhs.mModifier;
616 mKbPerSecond = rhs.mKbPerSecond;
617 }
618 return *this;
619}
620
623{
624 s << "b="
625 << mModifier
626 << Symbols::COLON[0] << mKbPerSecond
627 << Symbols::CRLF;
628 return s;
629}
630
631void
633{
634 pb.skipChar('b');
635 const char* anchor = pb.skipChar(Symbols::EQUALS[0]);
636
638 if (*pb.position() == Symbols::COLON[0])
639 {
640 pb.data(mModifier, anchor);
641
642 anchor = pb.skipChar(Symbols::COLON[0]);
643 mKbPerSecond = pb.integer();
644
645 skipEol(pb);
646 }
647 else
648 {
649 pb.fail(__FILE__, __LINE__);
650 }
651}
652
653SdpContents::Session::Time::Time(unsigned long start,
654 unsigned long stop)
655 : mStart(start),
656 mStop(stop)
657{}
658
659SdpContents::Session::Time::Time(const Time& rhs)
660 : mStart(rhs.mStart),
661 mStop(rhs.mStop)
662{}
663
666{
667 if (this != &rhs)
668 {
669 mStart = rhs.mStart;
670 mStop = rhs.mStop;
671 mRepeats = rhs.mRepeats;
672 }
673 return *this;
674}
675
678{
679 s << "t=" << mStart << Symbols::SPACE[0]
680 << mStop
681 << Symbols::CRLF;
682
683 for (list<Repeat>::const_iterator i = mRepeats.begin();
684 i != mRepeats.end(); ++i)
685 {
686 i->encode(s);
687 }
688 return s;
689}
690
691void
693{
694 pb.skipChar('t');
696
697 mStart = pb.uInt32();
699 mStop = pb.uInt32();
700
701 skipEol(pb);
702
703 while (!pb.eof() && *pb.position() == 'r')
704 {
705 addRepeat(Repeat());
706 mRepeats.back().parse(pb);
707 }
708}
709
710void
712{
713 mRepeats.push_back(repeat);
714}
715
716SdpContents::Session::Time::Repeat::Repeat(unsigned long interval,
717 unsigned long duration,
718 list<int> offsets)
719 : mInterval(interval),
720 mDuration(duration),
721 mOffsets(offsets)
722{}
723
726{
727 s << "r="
728 << mInterval << Symbols::SPACE[0]
729 << mDuration << 's';
730 for (list<int>::const_iterator i = mOffsets.begin();
731 i != mOffsets.end(); ++i)
732 {
733 s << Symbols::SPACE[0] << *i << 's';
734 }
735
736 s << Symbols::CRLF;
737 return s;
738}
739
740int
742{
743 int v = pb.integer();
744 if (!pb.eof())
745 {
746 switch (*pb.position())
747 {
748 case 's' :
749 pb.skipChar();
750 break;
751 case 'm' :
752 v *= 60;
753 pb.skipChar();
754 break;
755 case 'h' :
756 v *= 3600;
757 pb.skipChar();
758 break;
759 case 'd' :
760 v *= 3600*24;
761 pb.skipChar();
762 }
763 }
764 return v;
765}
766
767void
769{
770 pb.skipChar('r');
772
773 mInterval = parseTypedTime(pb);
775
776 mDuration = parseTypedTime(pb);
777
778 while (!pb.eof() && *pb.position() != Symbols::CR[0])
779 {
781
782 mOffsets.push_back(parseTypedTime(pb));
783 }
784
785 skipEol(pb);
786}
787
789 int _offset)
790 : time(_time),
791 offset(_offset)
792{}
793
795 : time(rhs.time),
796 offset(rhs.offset)
797{}
798
801{
802 if (this != &rhs)
803 {
804 time = rhs.time;
805 offset = rhs.offset;
806 }
807 return *this;
808}
809
813
815 : mAdjustments(rhs.mAdjustments)
816{}
817
820{
821 if (this != &rhs)
822 {
823 mAdjustments = rhs.mAdjustments;
824 }
825 return *this;
826}
827
830{
831 if (!mAdjustments.empty())
832 {
833 s << "z=";
834 bool first = true;
835 for (list<Adjustment>::const_iterator i = mAdjustments.begin();
836 i != mAdjustments.end(); ++i)
837 {
838 if (!first)
839 {
840 s << Symbols::SPACE[0];
841 }
842 first = false;
843 s << i->time << Symbols::SPACE[0]
844 << i->offset << 's';
845 }
846
847 s << Symbols::CRLF;
848 }
849 return s;
850}
851
852void
854{
855 pb.skipChar('z');
857
858 while (!pb.eof() && *pb.position() != Symbols::CR[0])
859 {
860 Adjustment adj(0, 0);
861 adj.time = pb.integer();
863 adj.offset = parseTypedTime(pb);
864 addAdjustment(adj);
865
866 if (!pb.eof() && *pb.position() == Symbols::SPACE[0])
867 {
868 pb.skipChar();
869 }
870 }
871
872 skipEol(pb);
873}
874
875void
877{
878 mAdjustments.push_back(adjust);
879}
880
882 : mMethod(NoEncryption),
883 mKey()
884{}
885
887 const Data& key)
888 : mMethod(method),
889 mKey(key)
890{}
891
893 : mMethod(rhs.mMethod),
894 mKey(rhs.mKey)
895{}
896
899{
900 if (this != &rhs)
901 {
902 mMethod = rhs.mMethod;
903 mKey = rhs.mKey;
904 }
905 return *this;
906}
907
908const char* KeyTypes[] = {"????", "prompt", "clear", "base64", "uri"};
909
912{
913 s << "k="
914 << KeyTypes[mMethod];
915 if (mMethod != Prompt)
916 {
917 s << Symbols::COLON[0] << mKey;
918 }
919 s << Symbols::CRLF;
920
921 return s;
922}
923
924void
926{
927 pb.skipChar('k');
928 const char* anchor = pb.skipChar(Symbols::EQUALS[0]);
929
931 if (!pb.eof())
932 {
933 Data p;
934 pb.data(p, anchor);
935 if (p == KeyTypes[Clear])
936 {
937 mMethod = Clear;
938 }
939 else if (p == KeyTypes[Base64])
940 {
941 mMethod = Base64;
942 }
943 else if (p == KeyTypes[UriKey])
944 {
945 mMethod = UriKey;
946 }
947
948 anchor = pb.skipChar(Symbols::COLON[0]);
950 pb.data(mKey, anchor);
951 }
952 else
953 {
954 pb.reset(anchor);
956
957 Data p;
958 pb.data(p, anchor);
959 if (p == KeyTypes[Prompt])
960 {
961 mMethod = Prompt;
962 }
963 }
964
965 skipEol(pb);
966}
967
969 const Origin& origin,
970 const Data& name)
971 : mVersion(version),
972 mOrigin(origin),
973 mName(name)
974{}
975
977{
978 *this = rhs;
979}
980
983{
984 if (this != &rhs)
985 {
986 mVersion = rhs.mVersion;
987 mOrigin = rhs.mOrigin;
988 mName = rhs.mName;
989 mMedia = rhs.mMedia;
990 mInformation = rhs.mInformation;
991 mUri = rhs.mUri;
992 mEmails = rhs.mEmails;
993 mPhones = rhs.mPhones;
994 mConnection = rhs.mConnection;
995 mBandwidths = rhs.mBandwidths;
996 mTimes = rhs.mTimes;
997 mTimezones = rhs.mTimezones;
998 mEncryption = rhs.mEncryption;
999 mAttributeHelper = rhs.mAttributeHelper;
1000
1001 for (MediumContainer::iterator i=mMedia.begin(); i != mMedia.end(); ++i)
1002 {
1003 i->setSession(this);
1004 }
1005 }
1006 return *this;
1007}
1008
1009void
1011{
1012 pb.skipChar('v');
1014 mVersion = pb.integer();
1015 skipEol(pb);
1016
1017 mOrigin.parse(pb);
1018
1019 pb.skipChar('s');
1020 const char* anchor = pb.skipChar(Symbols::EQUALS[0]);
1022 pb.data(mName, anchor);
1023 skipEol(pb);
1024
1025 if (!pb.eof() && *pb.position() == 'i')
1026 {
1027 pb.skipChar('i');
1028 const char* anchor = pb.skipChar(Symbols::EQUALS[0]);
1030 pb.data(mInformation, anchor);
1031 skipEol(pb);
1032 }
1033
1034 if (!pb.eof() && *pb.position() == 'u')
1035 {
1036 pb.skipChar('u');
1038 mUri.parse(pb);
1039 skipEol(pb);
1040 }
1041
1042 while (!pb.eof() && *pb.position() == 'e')
1043 {
1044 addEmail(Email());
1045 mEmails.back().parse(pb);
1046 }
1047
1048 while (!pb.eof() && *pb.position() == 'p')
1049 {
1050 addPhone(Phone());
1051 mPhones.back().parse(pb);
1052 }
1053
1054 if (!pb.eof() && *pb.position() == 'c')
1055 {
1056 mConnection.parse(pb);
1057 }
1058
1059 while (!pb.eof() && *pb.position() == 'b')
1060 {
1061 addBandwidth(Bandwidth());
1062 mBandwidths.back().parse(pb);
1063 }
1064
1065 while (!pb.eof() && *pb.position() == 't')
1066 {
1067 addTime(Time());
1068 mTimes.back().parse(pb);
1069 }
1070
1071 if (!pb.eof() && *pb.position() == 'z')
1072 {
1073 mTimezones.parse(pb);
1074 }
1075
1076 if (!pb.eof() && *pb.position() == 'k')
1077 {
1078 mEncryption.parse(pb);
1079 }
1080
1081 mAttributeHelper.parse(pb);
1082
1083 while (!pb.eof() && *pb.position() == 'm')
1084 {
1085 addMedium(Medium());
1086 mMedia.back().parse(pb);
1087 }
1088}
1089
1091SdpContents::Session::encode(EncodeStream& s) const
1092{
1093 s << "v=" << mVersion << Symbols::CRLF;
1094 mOrigin.encode(s);
1095 s << "s=" << mName << Symbols::CRLF;
1096
1097 if (!mInformation.empty())
1098 {
1099 s << "i=" << mInformation << Symbols::CRLF;
1100 }
1101
1102 if (!mUri.host().empty())
1103 {
1104 s << "u=";
1105 mUri.encode(s);
1106 s << Symbols::CRLF;
1107 }
1108
1109 for (list<Email>::const_iterator i = mEmails.begin();
1110 i != mEmails.end(); ++i)
1111 {
1112 i->encode(s);
1113 }
1114
1115 for (list<Phone>::const_iterator i = mPhones.begin();
1116 i != mPhones.end(); ++i)
1117 {
1118 i->encode(s);
1119 }
1120
1121 if (!mConnection.getAddress().empty())
1122 {
1123 mConnection.encode(s);
1124 }
1125
1126 for (list<Bandwidth>::const_iterator i = mBandwidths.begin();
1127 i != mBandwidths.end(); ++i)
1128 {
1129 i->encode(s);
1130 }
1131
1132 if (mTimes.empty())
1133 {
1134 s << "t=0 0" << Symbols::CRLF;
1135 }
1136 else
1137 {
1138 for (list<Time>::const_iterator i = mTimes.begin();
1139 i != mTimes.end(); ++i)
1140 {
1141 i->encode(s);
1142 }
1143 }
1144
1145 mTimezones.encode(s);
1146
1147 if (mEncryption.getMethod() != Encryption::NoEncryption)
1148 {
1149 mEncryption.encode(s);
1150 }
1151
1152 mAttributeHelper.encode(s);
1153
1154 for (MediumContainer::const_iterator i = mMedia.begin();
1155 i != mMedia.end(); ++i)
1156 {
1157 i->encode(s);
1158 }
1159
1160 return s;
1161}
1162
1163std::list<std::reference_wrapper<SdpContents::Session::Medium>>
1165{
1166 std::list<std::reference_wrapper<SdpContents::Session::Medium>> r;
1167 std::for_each(mMedia.begin(), mMedia.end(), [&r, &type](SdpContents::Session::Medium& m){
1168 if(m.name() == type)
1169 {
1170 r.push_back(std::ref(m));
1171 }
1172 });
1173 return r;
1174}
1175
1176void
1178{
1179 mEmails.push_back(email);
1180}
1181
1182void
1184{
1185 mTimes.push_back(t);
1186}
1187
1188void
1190{
1191 mPhones.push_back(phone);
1192}
1193
1194void
1196{
1197 mBandwidths.push_back(bandwidth);
1198}
1199
1200void
1202{
1203 mMedia.push_back(medium);
1204 mMedia.back().setSession(this);
1205}
1206
1207void
1209{
1210 mAttributeHelper.addAttribute(key, value);
1211
1212 if (key == rtpmap)
1213 {
1214 for (MediumContainer::iterator i = mMedia.begin();
1215 i != mMedia.end(); ++i)
1216 {
1217 i->mRtpMapDone = false;
1218 }
1219 }
1220}
1221
1222void
1224{
1225 mAttributeHelper.clearAttribute(key);
1226
1227 if (key == rtpmap)
1228 {
1229 for (MediumContainer::iterator i = mMedia.begin();
1230 i != mMedia.end(); ++i)
1231 {
1232 i->mRtpMapDone = false;
1233 }
1234 }
1235}
1236
1237bool
1239{
1240 return mAttributeHelper.exists(key);
1241}
1242
1243const list<Data>&
1245{
1246 return mAttributeHelper.getValues(key);
1247}
1248
1251{
1252 for(const Direction& k : Direction::ordered())
1253 {
1254 if(exists(k.name()))
1255 {
1256 return k;
1257 }
1258 }
1259
1260 return Direction::SENDRECV;
1261}
1262
1265{
1266 for(const Direction& k : Direction::ordered())
1267 {
1268 if(exists(k.name()))
1269 {
1270 return k;
1271 }
1272 }
1273
1274 if(mSession)
1275 {
1276 return mSession->getDirection();
1277 }
1278 else
1279 {
1280 return Direction::SENDRECV;
1281 }
1282}
1283
1284
1287{
1288 for(const Direction& k : Direction::ordered())
1289 {
1290 if(exists(k.name()))
1291 {
1292 return k;
1293 }
1294 }
1295
1296 return sessionDefault;
1297}
1298
1301{
1303 const Direction& sessionDefault = getDirection();
1304
1305 for(auto& m : mMedia)
1306 {
1307 directions.push_back(m.getDirection(sessionDefault).cref);
1308 }
1309
1310 return directions;
1311}
1312
1315{
1316 SdpContents::Session::DirectionList localDirections = getDirections();
1317 SdpContents::Session::DirectionList remoteDirections = remote.session().getDirections();
1318
1319 if(localDirections.size() != remoteDirections.size())
1320 {
1321 WarningLog(<<"SDP media count mismatch: local = " << localDirections.size()
1322 << " and remote = " << remoteDirections.size());
1323 }
1324
1326 auto iLocal = localDirections.cbegin();
1327 auto iRemote = remoteDirections.cbegin();
1328 while(iLocal != localDirections.cend() && iRemote != remoteDirections.cend())
1329 {
1330 const Direction& _iLocal = *iLocal;
1331 const Direction& _iRemote = *iRemote;
1332 if(_iLocal == Direction::INACTIVE || _iRemote == Direction::INACTIVE)
1333 {
1334 result.push_back(Direction::INACTIVE.cref);
1335 }
1336 else if(_iLocal == Direction::SENDONLY)
1337 {
1338 result.push_back(Direction::SENDONLY.cref);
1339 }
1340 else if(_iLocal == Direction::RECVONLY || _iRemote == Direction::SENDONLY)
1341 {
1342 result.push_back(Direction::RECVONLY.cref);
1343 }
1344 else if(_iRemote == Direction::RECVONLY)
1345 {
1346 result.push_back(Direction::SENDONLY.cref);
1347 }
1348 else
1349 {
1350 result.push_back(Direction::SENDRECV.cref);
1351 }
1352
1353 iLocal++;
1354 iRemote++;
1355 }
1356
1357 return result;
1358}
1359
1362 const std::set<Data> protocolTypes) const
1363{
1364 const Direction& sessionDefault = getDirection();
1365
1366 std::set<Data> directions;
1367 for(const auto& m : mMedia)
1368 {
1369 if((types.empty() || types.find(m.name())!=types.end()) &&
1370 (protocolTypes.empty() || protocolTypes.find(m.protocol())!=protocolTypes.end()) &&
1371 m.getConnections().size() > 0 &&
1372 m.port() != 0)
1373 {
1374 directions.insert(m.getDirection(sessionDefault).name());
1375 }
1376 }
1377
1378 // Identify the strongest direction attribute in the result set
1379 for(const Direction& k : Direction::ordered())
1380 {
1381 if(directions.find(k.name()) != directions.end())
1382 {
1383 return k;
1384 }
1385 }
1386 return sessionDefault;
1387}
1388
1389std::set<Data>
1391{
1392 std::set<Data> labels;
1393 for (std::list<resip::SdpContents::Session::Medium>::const_iterator it = mMedia.cbegin(); it != mMedia.cend(); it++)
1394 {
1396 if(m.name().caseInsensitiveTokenCompare("video") && m.exists("label"))
1397 {
1398 const std::list<Data>& _labels = m.getValues("label");
1399 labels.insert(_labels.begin(), _labels.end());
1400 }
1401 }
1402 return labels;
1403}
1404
1405bool
1407{
1408 std::set<resip::Data> mediumTransports;
1409 for(SdpContents::Session::MediumContainer::const_iterator it = mMedia.cbegin();
1410 it != mMedia.cend();
1411 it++)
1412 {
1413 const SdpContents::Session::Medium& m = *it;
1414 mediumTransports.insert(m.protocol());
1415 }
1416 return std::find(mediumTransports.cbegin(),
1417 mediumTransports.end(),
1418 "RTP/SAVPF") != mediumTransports.end();
1419}
1420
1421bool
1423{
1424 if(!exists("ice-options"))
1425 {
1426 return false;
1427 }
1428 auto opts = getValues("ice-options");
1429 for(auto opt = opts.cbegin(); opt != opts.cend(); opt++)
1430 {
1431 if(*opt == "trickle")
1432 {
1433 return true;
1434 }
1435 }
1436 return false;
1437}
1438
1439void
1440SdpContents::Session::transformCOMedia(const Data& setupDirection, const Data& cOMediaAttribute)
1441{
1442 for(SdpContents::Session::MediumContainer::iterator it = mMedia.begin();
1443 it != mMedia.end();
1444 it++)
1445 {
1447 m.port() = 9;
1448 m.addAttribute(cOMediaAttribute, setupDirection);
1449 }
1450}
1451
1452void
1454{
1455 SdpContents::Session::MediumContainer::iterator it = mMedia.begin();
1456 for(;it != mMedia.end(); it++)
1457 {
1459 if(holding)
1460 {
1461 if(m.exists("sendrecv"))
1462 {
1463 m.clearAttribute("sendrecv");
1464 m.addAttribute("sendonly");
1465 }
1466 if(m.exists("recvonly"))
1467 {
1468 m.clearAttribute("recvonly");
1469 m.addAttribute("inactive");
1470 }
1471 }
1472 else
1473 {
1474 if(m.exists("sendonly"))
1475 {
1476 m.clearAttribute("sendonly");
1477 m.addAttribute("sendrecv");
1478 }
1479 if(m.exists("inactive"))
1480 {
1481 m.clearAttribute("inactive");
1482 m.addAttribute("recvonly");
1483 }
1484 }
1485 }
1486}
1487
1490{
1491 for(auto _m = mMedia.cbegin(); _m != mMedia.end(); _m++)
1492 {
1493 if(_m->exists("mid") && _m->getValues("mid").front() == mid)
1494 {
1495 return &(*_m);
1496 }
1497 }
1498 return nullptr;
1499}
1500
1501std::shared_ptr<TrickleIceContents>
1503 unsigned int lineIndex, const Data& mid)
1504{
1505 std::shared_ptr<TrickleIceContents> ret;
1506 const Medium* m = getMediumByMid(mid);
1507 if(m && m->exists("ice-ufrag") && m->exists("ice-pwd"))
1508 {
1509 ret = std::make_shared<TrickleIceContents>();
1510 ret->addAttribute(Data("ice-ufrag"), m->getValues("ice-ufrag").front());
1511 ret->addAttribute(Data("ice-pwd"), m->getValues("ice-pwd").front());
1512 Medium _m(m->name(), m->port(), m->multicast(), m->protocol());
1513 _m.addAttribute("candidate", fragment.substr((Data::size_type)strlen("candidate:")));
1514 ret->addMedium(_m);
1515 }
1516 return ret;
1517}
1518
1520 unsigned long port,
1521 unsigned long multicast,
1522 const Data& protocol)
1523 : mSession(0),
1524 mName(name),
1525 mPort(port),
1526 mMulticast(multicast),
1527 mProtocol(protocol),
1528 mRtpMapDone(false)
1529{}
1530
1532 : mSession(0),
1533 mPort(0),
1534 mMulticast(1),
1535 mRtpMapDone(false)
1536{}
1537
1539 : mSession(0),
1540 mName(rhs.mName),
1541 mPort(rhs.mPort),
1542 mMulticast(rhs.mMulticast),
1543 mProtocol(rhs.mProtocol),
1544 mFormats(rhs.mFormats),
1545 mCodecs(rhs.mCodecs),
1546 mTransport(rhs.mTransport),
1547 mInformation(rhs.mInformation),
1548 mConnections(rhs.mConnections),
1549 mBandwidths(rhs.mBandwidths),
1550 mEncryption(rhs.mEncryption),
1551 mAttributeHelper(rhs.mAttributeHelper),
1552 mRtpMapDone(rhs.mRtpMapDone),
1553 mRtpMap(rhs.mRtpMap)
1554{
1555}
1556
1557
1560{
1561 if (this != &rhs)
1562 {
1563 mSession = 0;
1564 mName = rhs.mName;
1565 mPort = rhs.mPort;
1566 mMulticast = rhs.mMulticast;
1567 mProtocol = rhs.mProtocol;
1568 mFormats = rhs.mFormats;
1569 mCodecs = rhs.mCodecs;
1570 mTransport = rhs.mTransport;
1571 mInformation = rhs.mInformation;
1572 mConnections = rhs.mConnections;
1573 mBandwidths = rhs.mBandwidths;
1574 mEncryption = rhs.mEncryption;
1575 mAttributeHelper = rhs.mAttributeHelper;
1576 mRtpMapDone = rhs.mRtpMapDone;
1577 mRtpMap = rhs.mRtpMap;
1578 }
1579 return *this;
1580}
1581
1582void
1584{
1585 mPort = port;
1586}
1587
1588void
1589SdpContents::Session::Medium::setSession(Session* session)
1590{
1591 mSession = session;
1592}
1593
1594void
1596{
1597 pb.skipChar('m');
1598 const char* anchor = pb.skipChar(Symbols::EQUALS[0]);
1599
1601 pb.data(mName, anchor);
1602 pb.skipChar(Symbols::SPACE[0]);
1603
1604 mPort = pb.integer();
1605
1606 if (*pb.position() == Symbols::SLASH[0])
1607 {
1608 pb.skipChar();
1609 mMulticast = pb.integer();
1610 }
1611
1612 anchor = pb.skipChar(Symbols::SPACE[0]);
1614 pb.data(mProtocol, anchor);
1615
1616 while (*pb.position() != Symbols::CR[0] &&
1617 *pb.position() != Symbols::LF[0])
1618 {
1619 anchor = pb.skipChar(Symbols::SPACE[0]);
1621 if(pb.position() != anchor)
1622 {
1623 Data format;
1624 pb.data(format, anchor);
1625 addFormat(format);
1626 }
1627 }
1628
1629 skipEol(pb);
1630
1631 if (!pb.eof() && *pb.position() == 'i')
1632 {
1633 pb.skipChar('i');
1634 anchor = pb.skipChar(Symbols::EQUALS[0]);
1636 pb.data(mInformation, anchor);
1637
1638 skipEol(pb);
1639 }
1640
1641 while (!pb.eof() && *pb.position() == 'c')
1642 {
1643 addConnection(Connection());
1644 mConnections.back().parse(pb);
1645 if (!pb.eof() && *pb.position() == Symbols::SLASH[0])
1646 {
1647 // Note: we only get here if there was a /<number of addresses>
1648 // parameter following the connection address.
1649 pb.skipChar();
1650 int num = pb.integer();
1651
1652 if (num > 255)
1653 {
1654 pb.fail(__FILE__, __LINE__, "Too many connection addresses");
1655 }
1656
1657 Connection& con = mConnections.back();
1658 const Data& addr = con.getAddress();
1659 if (addr.empty())
1660 {
1661 pb.fail(__FILE__, __LINE__, "IP address expected");
1662 }
1663 Data::size_type i = addr.size() - 1;
1664 for (; i; i--)
1665 {
1666 if (addr[i] == '.' || addr[i] == ':') // ipv4 or ipv6
1667 {
1668 break;
1669 }
1670 }
1671
1672 if (addr[i] == '.') // add a number of ipv4 connections
1673 {
1674 Data before(addr.data(), i+1);
1675 ParseBuffer subpb(addr.data()+i+1, addr.size()-i-1);
1676 int after = subpb.integer();
1677
1678 for (int i = 1; i < num; i++)
1679 {
1680 addConnection(con);
1681 mConnections.back().mAddress = before + Data(after+i);
1682 }
1683 }
1684 if (addr[i] == ':') // add a number of ipv6 connections
1685 {
1686 Data before(addr.data(), i+1);
1687 int after = Helper::hex2integer(addr.data()+i+1);
1688 char hexstring[9];
1689
1690 for (int i = 1; i < num; i++)
1691 {
1692 addConnection(con);
1693 memset(hexstring, 0, sizeof(hexstring));
1694 Helper::integer2hex(hexstring, after+i, false /* supress leading zeros */);
1695 mConnections.back().mAddress = before + Data(hexstring);
1696 }
1697 }
1698
1699 skipEol(pb);
1700 }
1701 }
1702
1703 while (!pb.eof() && *pb.position() == 'b')
1704 {
1706 mBandwidths.back().parse(pb);
1707 }
1708
1709 if (!pb.eof() && *pb.position() == 'k')
1710 {
1711 mEncryption.parse(pb);
1712 }
1713
1714 mAttributeHelper.parse(pb);
1715}
1716
1719{
1720 s << "m="
1721 << mName << Symbols::SPACE[0]
1722 << mPort;
1723 if (mMulticast > 1)
1724 {
1725 s << Symbols::SLASH[0] << mMulticast;
1726 }
1727 s << Symbols::SPACE[0]
1728 << mProtocol;
1729
1730 for (list<Data>::const_iterator i = mFormats.begin();
1731 i != mFormats.end(); ++i)
1732 {
1733 s << Symbols::SPACE[0] << *i;
1734 }
1735
1736 if (!mCodecs.empty())
1737 {
1738 for (CodecContainer::const_iterator i = mCodecs.begin();
1739 i != mCodecs.end(); ++i)
1740 {
1741 s << Symbols::SPACE[0] << i->payloadType();
1742 }
1743 }
1744
1745 s << Symbols::CRLF;
1746
1747 if (!mInformation.empty())
1748 {
1749 s << "i=" << mInformation << Symbols::CRLF;
1750 }
1751
1752 for (list<Connection>::const_iterator i = mConnections.begin();
1753 i != mConnections.end(); ++i)
1754 {
1755 i->encode(s);
1756 }
1757
1758 for (list<Bandwidth>::const_iterator i = mBandwidths.begin();
1759 i != mBandwidths.end(); ++i)
1760 {
1761 i->encode(s);
1762 }
1763
1764 if (mEncryption.getMethod() != Encryption::NoEncryption)
1765 {
1766 mEncryption.encode(s);
1767 }
1768
1769 if (!mCodecs.empty())
1770 {
1771 // add codecs to information and attributes
1772 for (CodecContainer::const_iterator i = mCodecs.begin();
1773 i != mCodecs.end(); ++i)
1774 {
1775 // If codec is static (defined in RFC 3551) we probably shouldn't
1776 // add attributes for it. But some UAs do include them.
1777 //Codec::CodecMap& staticCodecs = Codec::getStaticCodecs();
1778 //if (staticCodecs.find(i->payloadType()) != staticCodecs.end())
1779 //{
1780 // continue;
1781 //}
1782
1783 s << "a=rtpmap:"
1784 << i->payloadType() << Symbols::SPACE[0] << *i
1785 << Symbols::CRLF;
1786 if (!i->parameters().empty())
1787 {
1788 s << "a=fmtp:"
1789 << i->payloadType() << Symbols::SPACE[0] << i->parameters()
1790 << Symbols::CRLF;
1791 }
1792 }
1793 }
1794
1795 mAttributeHelper.encode(s);
1796
1797 return s;
1798}
1799
1800void
1802{
1803 mFormats.push_back(format);
1804}
1805
1806void
1808{
1809 mConnections.clear();
1810 addConnection(connection);
1811}
1812
1813void
1815{
1816 mConnections.push_back(connection);
1817}
1818
1819void
1821{
1822 mBandwidths.clear();
1823 addBandwidth(bandwidth);
1824}
1825
1826void
1828{
1829 mBandwidths.push_back(bandwidth);
1830}
1831
1832void
1834{
1835 mAttributeHelper.addAttribute(key, value);
1836 if (key == rtpmap)
1837 {
1838 mRtpMapDone = false;
1839 }
1840}
1841
1842const list<SdpContents::Session::Connection>
1844{
1845 list<Connection> connections = const_cast<Medium*>(this)->getMediumConnections();
1846 // If there are connections specified at the medium level, then check if a session level
1847 // connection is present - if so then return it
1848 if (connections.empty() && mSession && !mSession->connection().getAddress().empty())
1849 {
1850 connections.push_back(mSession->connection());
1851 }
1852
1853 return connections;
1854}
1855
1856bool
1858{
1859 if (mAttributeHelper.exists(key))
1860 {
1861 return true;
1862 }
1863 return mSession && mSession->exists(key);
1864}
1865
1866const list<Data>&
1868{
1869 if (mAttributeHelper.exists(key))
1870 {
1871 return mAttributeHelper.getValues(key);
1872 }
1873 if (!mSession)
1874 {
1875 resip_assert(false);
1876 static list<Data> error;
1877 return error;
1878 }
1879 return mSession->getValues(key);
1880}
1881
1882list<Data>
1884{
1885 const list<Data>& mediumValues = mAttributeHelper.getValues(key);
1886 if (mSession && mSession->exists(key))
1887 {
1888 if (mediumValues.size() > 0)
1889 {
1890 // The attribute exists at both medium level and session level, merge lists for return
1891 list<Data> mergedValues = mediumValues;
1892 const list<Data>& sessionValues = mSession->getValues(key);
1893 mergedValues.insert(mergedValues.end(), sessionValues.begin(), sessionValues.end());
1894 return mergedValues;
1895 }
1896 // Attribute only exists at session level, return them
1897 return mSession->getValues(key);
1898 }
1899 // Attribute only exists at medium level, return them
1900 return mediumValues;
1901}
1902
1903void
1905{
1906 mAttributeHelper.clearAttribute(key);
1907 if (key == rtpmap)
1908 {
1909 mRtpMapDone = false;
1910 }
1911}
1912
1913void
1915{
1916 mFormats.clear();
1919 mCodecs.clear();
1920}
1921
1922void
1924{
1925 codecs();
1926 mCodecs.push_back(codec);
1927}
1928
1929
1932{
1933 return const_cast<Medium*>(this)->codecs();
1934}
1935
1938{
1939#if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1310) // CJ TODO fix
1940 resip_assert(0);
1941#else
1942 if (!mRtpMapDone)
1943 {
1944 // prevent recursion
1945 mRtpMapDone = true;
1946
1947 if (exists(rtpmap))
1948 {
1949 for (list<Data>::const_iterator i = getValues(rtpmap).begin();
1950 i != getValues(rtpmap).end(); ++i)
1951 {
1952 //DebugLog(<< "SdpContents::Session::Medium::getCodec(" << *i << ")");
1953 ParseBuffer pb(i->data(), i->size());
1954 int format = pb.integer();
1955 // pass to codec constructor for parsing
1956 // pass this for other codec attributes
1957 try
1958 {
1959 mRtpMap[format].parse(pb, *this, format);
1960 }
1961 catch (ParseException& e)
1962 {
1963 ErrLog(<<"Caught exception: "<< e);
1964 mRtpMap.erase(format);
1965 }
1966 }
1967 }
1968
1969 for (list<Data>::const_iterator i = mFormats.begin();
1970 i != mFormats.end(); ++i)
1971 {
1972 int mapKey = i->convertInt();
1973 RtpMap::const_iterator ri = mRtpMap.find(mapKey);
1974 if (ri != mRtpMap.end())
1975 {
1976 //DebugLog(<< "SdpContents::Session::Medium::getCodec[](" << ri->second << ")");
1977 mCodecs.push_back(ri->second);
1978 }
1979 else
1980 {
1981 // !kk! Is it a static format?
1982 Codec::CodecMap& staticCodecs = Codec::getStaticCodecs();
1983 Codec::CodecMap::const_iterator ri = staticCodecs.find(mapKey);
1984 if (ri != staticCodecs.end())
1985 {
1986 //DebugLog(<< "Found static codec for format: " << mapKey);
1987 Codec codec(ri->second);
1988
1989 // Look for format parameters, and assign
1990 codec.assignFormatParameters(*this);
1991
1992 mCodecs.push_back(codec);
1993 }
1994 }
1995 }
1996
1997 // don't store twice
1998 mFormats.clear();
1999 mAttributeHelper.clearAttribute(rtpmap);
2000 mAttributeHelper.clearAttribute(fmtp); // parsed out in codec.parse
2001 }
2002#endif
2003
2004 return mCodecs;
2005}
2006
2008const Codec&
2010{
2011 const CodecContainer& internalCodecList = codecs();
2012 resip::SdpContents::Session::Medium::CodecContainer::const_iterator sIter;
2013 resip::SdpContents::Session::Medium::CodecContainer::const_iterator sEnd = internalCodecList.end();
2014 resip::SdpContents::Session::Medium::CodecContainer::const_iterator eIter;
2015 resip::SdpContents::Session::Medium::CodecContainer::const_iterator eEnd = codecList.end();
2016 for (eIter = codecList.begin(); eIter != eEnd ; ++eIter)
2017 {
2018 for (sIter = internalCodecList.begin(); sIter != sEnd; ++sIter)
2019 {
2020 if (*sIter == *eIter)
2021 {
2022 if (pMatchingCodec)
2023 {
2024 *pMatchingCodec = *eIter;
2025 }
2026 return *sIter;
2027 }
2028 }
2029 }
2030 return emptyCodec;
2031}
2032
2033const Codec&
2035{
2036 if (&medium == this)
2037 {
2038 return codecs().front();
2039 }
2040 else
2041 {
2042 return findFirstMatchingCodecs(medium.codecs(), pMatchingCodec);
2043 }
2044}
2045
2046const Codec&
2048{
2049 const CodecContainer& codecList = codecs();
2050 for (CodecContainer::const_iterator i = codecList.begin(); i != codecList.end(); i++)
2051 {
2053 {
2054 return *i;
2055 }
2056 }
2057 return emptyCodec;
2058}
2059
2060int
2062{
2063 const Codec& telephoneEventCodec = findTelephoneEventPayloadCodec();
2064 if (!(telephoneEventCodec == emptyCodec))
2065 {
2066 return telephoneEventCodec.payloadType();
2067 }
2068 return -1;
2069}
2070
2072 unsigned long rate,
2073 const Data& parameters,
2074 const Data& encodingParameters)
2075 : mName(name),
2076 mRate(rate),
2077 mPayloadType(-1),
2078 mParameters(parameters),
2079 mEncodingParameters(encodingParameters)
2080{
2081}
2082
2084 : mName(rhs.mName),
2085 mRate(rhs.mRate),
2086 mPayloadType(rhs.mPayloadType),
2087 mParameters(rhs.mParameters),
2088 mEncodingParameters(rhs.mEncodingParameters)
2089{
2090}
2091
2092Codec::Codec(const Data& name, int payloadType, int rate)
2093 : mName(name),
2094 mRate(rate),
2095 mPayloadType(payloadType)
2096{
2097}
2098
2099Codec&
2101{
2102 if (this != &rhs)
2103 {
2104 mName = rhs.mName;
2105 mRate = rhs.mRate;
2106 mPayloadType = rhs.mPayloadType;
2107 mParameters = rhs.mParameters;
2108 mEncodingParameters = rhs.mEncodingParameters;
2109 }
2110 return *this;
2111}
2112
2113void
2115 const SdpContents::Session::Medium& medium,
2116 int payloadType)
2117{
2118 const char* anchor = pb.skipWhitespace();
2120 mName = pb.data(anchor);
2121 if(!pb.eof())
2122 {
2123 pb.skipChar(Symbols::SLASH[0]);
2124 mRate = pb.integer();
2126 }
2127 if(!pb.eof() && *pb.position() == Symbols::SLASH[0])
2128 {
2129 anchor = pb.skipChar(Symbols::SLASH[0]);
2130 pb.skipToEnd();
2131 mEncodingParameters = pb.data(anchor);
2132 }
2133 mPayloadType = payloadType;
2134
2135 assignFormatParameters(medium);
2136}
2137
2138void
2140{
2141 // get parameters if they exist
2142 if (medium.exists(fmtp))
2143 {
2144 for (list<Data>::const_iterator i = medium.getValues(fmtp).begin();
2145 i != medium.getValues(fmtp).end(); ++i)
2146 {
2147 try
2148 {
2149 ParseBuffer pb(i->data(), i->size());
2150 int payload = pb.integer();
2151 if (payload == mPayloadType)
2152 {
2153 const char* anchor = pb.skipWhitespace();
2154 pb.skipToEnd();
2155 mParameters = pb.data(anchor);
2156 break;
2157 }
2158 }
2159 catch (ParseException &pe)
2160 {
2161 InfoLog(<<"Caught exception when parsing a=fmtp: "<< pe);
2162 }
2163 }
2164 }
2165}
2166
2167const Data&
2169{
2170 return mName;
2171}
2172
2173int
2175{
2176 return mRate;
2177}
2178
2180{
2181 if (! sStaticCodecsCreated)
2182 {
2183 //
2184 // Build map of static codecs as defined in RFC 3551
2185 //
2186 sStaticCodecs = std::unique_ptr<CodecMap>(new CodecMap);
2187
2188 // Audio codecs
2189 sStaticCodecs->insert(make_pair(0,Codec("PCMU",0,8000)));
2190 sStaticCodecs->insert(make_pair(3,Codec("GSM",3,8000)));
2191 sStaticCodecs->insert(make_pair(4,Codec("G723",4,8000)));
2192 sStaticCodecs->insert(make_pair(5,Codec("DVI4",5,8000)));
2193 sStaticCodecs->insert(make_pair(6,Codec("DVI4",6,16000)));
2194 sStaticCodecs->insert(make_pair(7,Codec("LPC",7,8000)));
2195 sStaticCodecs->insert(make_pair(8,Codec("PCMA",8,8000)));
2196 sStaticCodecs->insert(make_pair(9,Codec("G722",9,8000)));
2197 sStaticCodecs->insert(make_pair(10,Codec("L16-2",10,44100)));
2198 sStaticCodecs->insert(make_pair(11,Codec("L16-1",11,44100)));
2199 sStaticCodecs->insert(make_pair(12,Codec("QCELP",12,8000)));
2200 sStaticCodecs->insert(make_pair(13,Codec("CN",13,8000)));
2201 sStaticCodecs->insert(make_pair(14,Codec("MPA",14,90000)));
2202 sStaticCodecs->insert(make_pair(15,Codec("G728",15,8000)));
2203 sStaticCodecs->insert(make_pair(16,Codec("DVI4",16,11025)));
2204 sStaticCodecs->insert(make_pair(17,Codec("DVI4",17,22050)));
2205 sStaticCodecs->insert(make_pair(18,Codec("G729",18,8000)));
2206
2207 // Video or audio/video codecs
2208 sStaticCodecs->insert(make_pair(25,Codec("CelB",25,90000)));
2209 sStaticCodecs->insert(make_pair(26,Codec("JPEG",26,90000)));
2210 sStaticCodecs->insert(make_pair(28,Codec("nv",28,90000)));
2211 sStaticCodecs->insert(make_pair(31,Codec("H261",31,90000)));
2212 sStaticCodecs->insert(make_pair(32,Codec("MPV",32,90000)));
2213 sStaticCodecs->insert(make_pair(33,Codec("MP2T",33,90000)));
2214 sStaticCodecs->insert(make_pair(34,Codec("H263",34,90000)));
2215
2216 sStaticCodecsCreated = true;
2217 }
2218 return *(sStaticCodecs.get());
2219}
2220
2221bool
2222resip::operator==(const Codec& lhs, const Codec& rhs)
2223{
2224 static Data defaultEncodingParameters(Data("1")); // Default for audio streams (1-Channel)
2225 return (isEqualNoCase(lhs.mName, rhs.mName) && lhs.mRate == rhs.mRate &&
2226 (lhs.mEncodingParameters == rhs.mEncodingParameters ||
2227 (lhs.mEncodingParameters.empty() && rhs.mEncodingParameters == defaultEncodingParameters) ||
2228 (lhs.mEncodingParameters == defaultEncodingParameters && rhs.mEncodingParameters.empty())));
2229}
2230
2231bool
2232resip::operator!=(const Codec& lhs, const Codec& rhs)
2233{
2234 return !operator==(lhs, rhs);
2235}
2236
2238resip::operator<<(EncodeStream& str, const Codec& codec)
2239{
2240 str << codec.mName;
2241 str << Symbols::SLASH[0];
2242 str << codec.mRate;
2243 if(!codec.mEncodingParameters.empty())
2244 {
2245 str << Symbols::SLASH[0];
2246 str << codec.mEncodingParameters;
2247 }
2248 return str;
2249}
2250
2251const Codec Codec::ULaw_8000("PCMU", 0, 8000);
2252const Codec Codec::GSM_8000("GSM", 3, 8000);
2253const Codec Codec::G723_8000("G723", 4, 8000);
2254const Codec Codec::ALaw_8000("PCMA", 8, 8000);
2255const Codec Codec::G722_8000("G722", 9, 8000);
2256const Codec Codec::CN("CN", 13, 8000);
2257const Codec Codec::G729_8000("G729", 18, 8000);
2258const Codec Codec::H263("H263", 34, 90000);
2259
2260const Codec Codec::TelephoneEvent("telephone-event", 101, 8000);
2261const Codec Codec::FrfDialedDigit("frf-dialed-event",102, 8000);
2262
2263bool Codec::sStaticCodecsCreated = false;
2264std::unique_ptr<Codec::CodecMap> Codec::sStaticCodecs;
2265
2266/* ====================================================================
2267 * The Vovida Software License, Version 1.0
2268 *
2269 * Copyright (c) 2026, SIP Spectrum, Inc. https://www.sipspectrum.com
2270 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
2271 *
2272 * Redistribution and use in source and binary forms, with or without
2273 * modification, are permitted provided that the following conditions
2274 * are met:
2275 *
2276 * 1. Redistributions of source code must retain the above copyright
2277 * notice, this list of conditions and the following disclaimer.
2278 *
2279 * 2. Redistributions in binary form must reproduce the above copyright
2280 * notice, this list of conditions and the following disclaimer in
2281 * the documentation and/or other materials provided with the
2282 * distribution.
2283 *
2284 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
2285 * and "Vovida Open Communication Application Library (VOCAL)" must
2286 * not be used to endorse or promote products derived from this
2287 * software without prior written permission. For written
2288 * permission, please contact vocal@vovida.org.
2289 *
2290 * 4. Products derived from this software may not be called "VOCAL", nor
2291 * may "VOCAL" appear in their name, without prior written
2292 * permission of Vovida Networks, Inc.
2293 *
2294 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
2295 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2296 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
2297 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
2298 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
2299 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
2300 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2301 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2302 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
2303 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2304 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
2305 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2306 * DAMAGE.
2307 *
2308 * ====================================================================
2309 *
2310 * This software consists of voluntary contributions made by Vovida
2311 * Networks, Inc. and many individuals on behalf of Vovida Networks,
2312 * Inc. For more information on Vovida Networks, Inc., please see
2313 * <http://www.vovida.org/>.
2314 *
2315 */
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
void parseEorP(ParseBuffer &pb, Data &eOrp, Data &freeText)
const char * KeyTypes[]
static Codec emptyCodec
static const Data rtpmap("rtpmap")
const char * NetworkType[]
int parseTypedTime(ParseBuffer &pb)
static const Data fmtp("fmtp")
static Data nullOrigin("0.0.0.0")
static const struct nv types[]
Definition adig.c:80
static constexpr size_t MaxParsedAttributeNames
parse() fails a section with more distinct attribute names than this
void clearAttribute(const Data &key)
const std::list< Data > & getValues(const Data &key) const
EncodeStream & encode(EncodeStream &s) const
AttributeHelper & operator=(const AttributeHelper &rhs)
void addAttribute(const Data &key, const Data &value=Data::Empty)
bool exists(const Data &key) const
void parse(ParseBuffer &pb)
Base class for all SIP body types.
Definition Contents.hxx:51
bool exists(const HeaderBase &headerType) const
checks to see if a header is present
Definition Contents.cxx:189
Contents & operator=(const Contents &rhs)
Assignment operator.
Definition Contents.cxx:71
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
Data substr(size_type first, size_type count=Data::npos) const
Copies a portion of this Data into a new Data.
Definition Data.cxx:1932
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_t size_type
Definition Data.hxx:94
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
bool caseInsensitiveTokenCompare(const Data &rhs) const
Definition Data.hxx:997
static unsigned int hex2integer(const char *_s)
Converts a character string containing a hexidecimal value into an unsigned int.
Definition Helper.cxx:92
static void integer2hex(char *_d, unsigned int _s, bool _l=true)
Converts an interger in a character string containing the hexidecimal representation of the integer.
Definition Helper.cxx:52
Represents the "media-type" element in the RFC 3261 grammar.
Definition Mime.hxx:20
Provides string-parsing functionality with protection from buffer overflow.
uint32_t uInt32()
dcm! – merge these, ask about length checks
CurrentPosition skipToEnd()
CurrentPosition skipWhitespace()
CurrentPosition skipToChar(char c)
CurrentPosition position() const
void reset(const char *pos)
CurrentPosition skipToOneOf(const char *cs)
CurrentPosition skipChar()
void fail(const char *file, unsigned int line, const Data &errmsg=Data::Empty) const
void data(Data &data, const char *start) const
make the passed in data share memory with the buffer (uses Data::Share)
const char * skipToEndQuote(char quote='"')
BaseException used to indicate parse failures.
Process optional b= (bandwidth) lines in SDP.
Bandwidth & operator=(const Bandwidth &rhs)
EncodeStream & encode(EncodeStream &) const
parameters for a specific codec are stored in this class.
Codec & operator=(const Codec &codec)
void assignFormatParameters(const SdpContents::Session::Medium &medium)
void parse(ParseBuffer &pb, const SdpContents::Session::Medium &medium, int payLoadType)
const Data & getName() const
returns the name of the codec.
int payloadType() const
returns the RTP payload type associated with this codec.
int getRate() const
returns the name of the codec.
Process c= (connection) lines in SDP.
const Data & getAddress() const
returns the connection address
void setAddress(const Data &host, AddrType type=IP4)
set the address for the connection
EncodeStream & encode(EncodeStream &) const
Connection & operator=(const Connection &rhs)
process e= (email) lines in the SDP
EncodeStream & encode(EncodeStream &) const
Email & operator=(const Email &rhs)
process k= (encryption key) line
EncodeStream & encode(EncodeStream &) const
Encryption & operator=(const Encryption &rhs)
process m= (media announcement) blocks
const Codec & findTelephoneEventPayloadCodec() const
finds the telephone-event codec
Medium & operator=(const Medium &rhs)
const std::list< Connection > getConnections() const
get a list of Connection objects, including the Session's c= line.
const Codec & findFirstMatchingCodecs(const CodecContainer &codecs, Codec *pMatchingCodec=0) const
void addBandwidth(const Bandwidth &bandwidth)
void setConnection(const Connection &connection)
set the media connection line.
void addAttribute(const Data &key, const Data &value=Data::Empty)
add a media attribute line
const std::list< Data > & getValues(const Data &key) const
get the attribute values corresponding to the key - returns the attributes set on this medium,...
EncodeStream & encode(EncodeStream &) const
bool exists(const Data &key) const
tests if an a= key is present in the media section
void addConnection(const Connection &connection)
add a media connection line.
void setBandwidth(const Bandwidth &bandwidth)
int port() const
return the base port
void addCodec(const Codec &codec)
add a codec to the m= line
void addFormat(const Data &format)
add a format identifier to the m= line
void setPort(int port)
change the base port
std::list< Data > getMergedValues(const Data &key) const
get the attribute values corresponding to the key - returns both the attributes set on this medium,...
const CodecContainer & codecs() const
preferred codec/format interface
void clearCodecs()
remove all codecs from SDP
int multicast() const
get the number of transport port pairs
const Direction & getDirection() const
const Data & protocol() const
return the transport protocol
const Data & name() const
return the media type
int findTelephoneEventPayloadType() const
finds the telephone-event payload type
void clearAttribute(const Data &key)
erase all attributes for a given key
processes o= lines in SDP
EncodeStream & encode(EncodeStream &) const
void setAddress(const Data &host, AddrType type=IP4)
set the address for the session
Origin & operator=(const Origin &rhs)
process p= (phone number) lines in the SDP
EncodeStream & encode(EncodeStream &) const
Phone & operator=(const Phone &rhs)
EncodeStream & encode(EncodeStream &) const
Process t= (start/stop time) lines in SDP.
Time & operator=(const Time &rhs)
void addRepeat(const Repeat &repeat)
EncodeStream & encode(EncodeStream &) const
specify the time at which a timezone shift will occur and the offset, in seconds
Adjustment & operator=(const Adjustment &rhs)
process z= (timezone) lines
void addAdjustment(const Adjustment &adjusment)
EncodeStream & encode(EncodeStream &) const
Timezones & operator=(const Timezones &rhs)
Provides an interface to read and modify SDP bodies.
void addMedium(const Medium &medium)
add an m= (Medium) section to session
void addPhone(const Phone &phone)
add a p= (phone number) line to session
void addTime(const Time &t)
add a t= (Time) line to session
std::shared_ptr< TrickleIceContents > makeIceFragment(const Data &fragment, unsigned int lineIndex, const Data &mid)
based on the original SDP in this instances of SdpContents, find the relevant ICE and m= line(s),...
std::list< std::reference_wrapper< Medium > > getMediaByType(const Data &type)
return session Media lines filtered by type
const Direction & getDirection() const
int version() const
return session version
void parse(ParseBuffer &pb)
DirectionList getNetDirections(const SdpContents &remote) const
const Origin & origin() const
return session Origin line
Session & operator=(const Session &rhs)
const std::list< Data > & getValues(const Data &key) const
get the attribute values corresponding to the key
std::vector< std::reference_wrapper< const SdpContents::Session::Direction > > DirectionList
const Data & name() const
return session name
bool isWebRTC() const
determine if the SDP appears to conform to WebRTC
void addBandwidth(const Bandwidth &bandwidth)
add a b= (Bandwidth) line to session
std::set< Data > getMediaStreamLabels() const
retrieve label (RFC 4574) attributes in a set
void transformLocalHold(bool holding)
change the direction attributes as if the peer who created the SDP is on hold or not
const Connection & connection() const
return session Connection
const Medium * getMediumByMid(const Data &mid) const
find the Medium with given mid value
EncodeStream & encode(EncodeStream &) const
void clearAttribute(const Data &key)
erase all attributes for a given key
void addEmail(const Email &email)
add an e= (email) line to session
bool exists(const Data &key) const
tests if an a= key is present in the session
bool isTrickleIceSupported() const
determine if ice-options:trickle is present
DirectionList getDirections() const
void transformCOMedia(const Data &setupDirection="active", const Data &cOMediaAttribute="setup")
apply RFC 4145 COMEDIA transform
void addAttribute(const Data &key, const Data &value=Data::Empty)
add a session attribute line
Provides an interface to process and generate SDP bodies (MIME content-type application/sdp).
virtual EncodeStream & encodeParsed(EncodeStream &str) const
static bool init()
virtual void parse(ParseBuffer &pb)
static const SdpContents Empty
virtual Contents * clone() const
duplicate an SdpContents object
static const Mime & getStaticType()
Session & session()
get the parsed SDP
SdpContents & operator=(const SdpContents &rhs)
static const char * LF
Definition Symbols.hxx:24
static const char * RPAREN
Definition Symbols.hxx:38
static const char * LPAREN
Definition Symbols.hxx:37
static const char * SPACE
Definition Symbols.hxx:34
static const char * COLON
Definition Symbols.hxx:15
static const char * CR
Definition Symbols.hxx:23
static const char * SLASH
Definition Symbols.hxx:32
static const char * RA_QUOTE
Definition Symbols.hxx:28
static const char * EQUALS
Definition Symbols.hxx:29
static const char * TAB
Definition Symbols.hxx:25
static const char * CRLF
Definition Symbols.hxx:21
dcm! – add UnusedChecking(_enum) below;
bool isEqualNoCase(const Data &left, const Data &right)
Definition Data.hxx:1116
unsigned long long uint64_t
Definition Stun.hxx:39
void skipEol(ParseBuffer &pb)
EncodeStream & operator<<(EncodeStream &strm, const BaseException &e)
bool operator==(const char *lhs, const Data &rhs)
Definition Data.hxx:1197
bool operator!=(const Data &lhs, const Data &rhs)
Definition Data.hxx:1187
#define EncodeStream