1 /*
2  *parse or be parsed
3  */
4 
5 
6 #include <stdio.h>
7 #include <fcntl.h>
8 #include <assert.h>
9 #include <netinet/in.h>
10 #include <arpa/inet.h>
11 
12 #include "AmConfig.h"
13 #include "AmSdp.h"
14 #include "AmUtils.h"
15 #include "AmPlugIn.h"
16 #include "AmSession.h"
17 
18 #include "amci/amci.h"
19 #include "log.h"
20 
21 #include <stdexcept>
22 
23 #include <string>
24 #include <map>
25 using std::string;
26 using std::map;
27 
28 #include <cctype>
29 #include <algorithm>
30 
31 // Not on Solaris!
32 #if !defined (__SVR4) && !defined (__sun)
33 #include "strings.h"
34 #endif
35 
36 #define CR   '\r'
37 #define LF   '\n'
38 #define CRLF "\r\n"
39 
40 static void parse_session_attr(AmSdp* sdp_msg, char* s, char** next);
41 static bool parse_sdp_line_ex(AmSdp* sdp_msg, char*& s);
42 static char* parse_sdp_connection(AmSdp* sdp_msg, char* s, char t);
43 static void parse_sdp_media(AmSdp* sdp_msg, char* s);
44 static char* parse_sdp_attr(AmSdp* sdp_msg, char* s);
45 static void parse_sdp_origin(AmSdp* sdp_masg, char* s);
46 
47 inline char* get_next_line(char* s);
48 inline char* skip_till_next_line(char* s, size_t& line_len);
49 static char* is_eql_next(char* s);
50 static char* parse_until(char* s, char end);
51 static char* parse_until(char* s, char* end, char c);
52 static bool contains(char* s, char* next_line, char c);
53 static bool is_wsp(char s);
54 
55 static MediaType media_type(std::string media);
56 static TransProt transport_type(std::string transport);
57 static bool attr_check(std::string attr);
58 
59 enum parse_st {SDP_DESCR, SDP_MEDIA};
60 enum sdp_connection_st {NET_TYPE, ADDR_TYPE, IP4, IP6};
61 enum sdp_media_st {MEDIA, PORT, PROTO, FMT};
62 enum sdp_attr_rtpmap_st {TYPE, ENC_NAME, CLK_RATE, ENC_PARAM};
63 enum sdp_attr_fmtp_st {FORMAT, FORMAT_PARAM};
64 enum sdp_origin_st {USER, ID, VERSION_ST, NETTYPE, ADDR, UNICAST_ADDR};
65 
66 // inline functions
67 
net_t_2_str(int nt)68 inline string net_t_2_str(int nt)
69 {
70   switch(nt){
71   case NT_IN: return "IN";
72   default: return "<unknown network type>";
73   }
74 }
75 
addr_t_2_str(int at)76 inline string addr_t_2_str(int at)
77 {
78   switch(at){
79   case AT_V4: return "IP4";
80   case AT_V6: return "IP6";
81   default: return "<unknown address type>";
82   }
83 }
84 
85 
media_t_2_str(int mt)86 inline string media_t_2_str(int mt)
87 {
88   switch(mt){
89   case MT_AUDIO: return "audio";
90   case MT_VIDEO: return "video";
91   case MT_APPLICATION: return "application";
92   case MT_TEXT: return "text";
93   case MT_MESSAGE: return "message";
94   case MT_IMAGE: return "image";
95   default: return "<unknown media type>";
96   }
97 }
98 
transport_p_2_str(int tp)99 inline string transport_p_2_str(int tp)
100 {
101   switch(tp){
102   case TP_RTPAVP: return "RTP/AVP";
103   case TP_RTPAVPF: return "RTP/AVPF";
104   case TP_UDP: return "udp";
105   case TP_RTPSAVP: return "RTP/SAVP";
106   case TP_RTPSAVPF: return "RTP/SAVPF";
107   case TP_UDPTLSRTPSAVP: return "UDP/TLS/RTP/SAVP";
108   case TP_UDPTLSRTPSAVPF: return "UDP/TLS/RTP/SAVPF";
109   case TP_UDPTL: return "udptl";
110   default: return "<unknown media type>";
111   }
112 }
113 
SdpConnection()114 SdpConnection::SdpConnection()
115   : network(NT_OTHER), addrType(AT_NONE), address() {
116   memset(&ipv4, 0, sizeof(ipv4));
117   memset(&ipv6, 0, sizeof(ipv6));
118 }
119 
operator ==(const SdpConnection & other) const120 bool SdpConnection::operator == (const SdpConnection& other) const
121 {
122   return network == other.network && addrType == other.addrType
123     && address == other.address;
124 }
125 
operator ==(const SdpOrigin & other) const126 bool SdpOrigin::operator == (const SdpOrigin& other) const
127 {
128   return user == other.user && sessId == other.sessId
129     && sessV == other.sessV && conn == other.conn;
130 }
131 
operator ==(const SdpPayload & other) const132 bool SdpPayload::operator == (const SdpPayload& other) const
133 {
134   return payload_type == other.payload_type && encoding_name == other.encoding_name
135     && clock_rate == other.clock_rate && encoding_param == other.encoding_param;
136 }
137 
debugPrint() const138 string SdpConnection::debugPrint() const {
139   return addr_t_2_str(addrType) + " " + address;
140 }
141 
debugPrint() const142 string SdpMedia::debugPrint() const {
143   string payload_list;
144   for(std::vector<SdpPayload>::const_iterator it=
145 	payloads.begin(); it!= payloads.end(); it++) {
146     if (it != payloads.begin())
147       payload_list+=" ";
148     payload_list+=int2str(it->payload_type);
149   }
150   return "port "+int2str(port) + ", payloads: "+payload_list;
151 }
152 
type2str(int type)153 string SdpMedia::type2str(int type)
154 {
155   return media_t_2_str(type);
156 }
157 
operator ==(int r)158 bool SdpPayload::operator == (int r)
159 {
160   DBG("pl == r: payload_type = %i; r = %i\n", payload_type, r);
161   return payload_type == r;
162 }
163 
print() const164 string SdpAttribute::print() const
165 {
166   if (value.empty())
167     return "a="+attribute+CRLF;
168   else
169     return "a="+attribute+":"+value+CRLF;
170 }
171 
operator ==(const SdpAttribute & other) const172 bool SdpAttribute::operator==(const SdpAttribute& other) const
173 {
174   return attribute==other.attribute && (value.empty() || (value==other.value));
175 }
176 
operator ==(const SdpMedia & other) const177 bool SdpMedia::operator == (const SdpMedia& other) const
178 {
179   if (payloads.empty()) {
180     if (!other.payloads.empty())
181       return false;
182 
183   } else if (other.payloads.empty()) {
184     return false;
185 
186   } else {
187     std::pair<vector<SdpPayload>::const_iterator, vector<SdpPayload>::const_iterator> pl_mismatch
188       = std::mismatch(payloads.begin(), payloads.end(), other.payloads.begin());
189 
190     if (pl_mismatch.first != payloads.end() || pl_mismatch.second != other.payloads.end())
191       return false;
192   }
193 
194   if (attributes.empty()) {
195     if (!other.attributes.empty()) {
196       return false;
197     }
198   } else {
199     std::pair<vector<SdpAttribute>::const_iterator, vector<SdpAttribute>::const_iterator> a_mismatch
200       = std::mismatch(attributes.begin(), attributes.end(), other.attributes.begin());
201 
202     if (a_mismatch.first != attributes.end() || a_mismatch.second != other.attributes.end())
203       return false;
204   }
205 
206   return type == other.type && port == other.port && nports == other.nports
207     && transport == other.transport && conn == other.conn && dir == other.dir
208     && send == other.send && recv == other.recv;
209 }
210 
211 //
212 // class RtcpAddress: Methods
213 //
parse(const string & src)214 bool RtcpAddress::parse(const string &src)
215 {
216   port = 0;
217   nettype.clear();
218   addrtype.clear();
219   address.clear();
220 
221   int len = src.size();
222   if (len < 1) return false;
223 
224   enum { PORT, NET_TYPE, ADDR_TYPE, ADDR } s = PORT;
225 
226   // parsing (somehow) according to RFC 3605
227   //    rtcp-attribute =  "a=rtcp:" port  [nettype space addrtype space
228   //                             connection-address] CRLF
229   // nettype, addrtype is ignored
230   for (int i = 0; i < len; ++i) {
231     switch (s) {
232 
233       case (PORT):
234         if (src[i] >= '0' && src[i] <= '9') port = port * 10 + (src[i] - '0');
235         else if (src[i] == ' ') s = NET_TYPE;
236         else return false; // error
237         break;
238 
239       case NET_TYPE:
240           if (src[i] == ' ') s = ADDR_TYPE;
241           else nettype += src[i];
242           break;
243 
244       case ADDR_TYPE:
245           if (src[i] == ' ') s = ADDR;
246           else addrtype += src[i];
247           break;
248 
249       case ADDR:
250           address += src[i];
251           break;
252     }
253   }
254   return s == PORT ||
255     (s == ADDR && !address.empty());
256   // FIXME: nettype, addrtype and addr should be verified
257 }
258 
print()259 string RtcpAddress::print()
260 {
261   string s(int2str(port));
262   if (!address.empty())
263     s += " IN " + addrtype + " " + address;
264   return s;
265 }
266 
RtcpAddress(const string & attr_value)267 RtcpAddress::RtcpAddress(const string &attr_value): port(0)
268 {
269   if (!parse(attr_value))
270     throw std::runtime_error("can't parse rtcp attribute value");
271 }
272 
273 
274 //
275 // class AmSdp: Methods
276 //
AmSdp()277 AmSdp::AmSdp()
278   : version(0),
279     origin(),
280     sessionName(),
281     conn(),
282     media()
283 {
284   origin.user = "sems";
285   origin.sessId = get_random();
286   origin.sessV = get_random();
287 }
288 
AmSdp(const AmSdp & p_sdp_msg)289 AmSdp::AmSdp(const AmSdp& p_sdp_msg)
290   : version(p_sdp_msg.version),
291     origin(p_sdp_msg.origin),
292     sessionName(p_sdp_msg.sessionName),
293     conn(p_sdp_msg.conn),
294     attributes(p_sdp_msg.attributes),
295     media(p_sdp_msg.media)
296 {
297 }
298 
parse(const char * _sdp_msg)299 int AmSdp::parse(const char* _sdp_msg)
300 {
301   char* s = (char*)_sdp_msg;
302   clear();
303 
304   bool ret = parse_sdp_line_ex(this,s);
305 
306   if(!ret && conn.address.empty()){
307     for(vector<SdpMedia>::iterator it = media.begin();
308 	!ret && (it != media.end()); ++it)
309       ret = it->conn.address.empty();
310 
311     if(ret){
312       ERROR("A connection field must be field must be present in every\n");
313       ERROR("media description or at the session level.\n");
314     }
315   }
316 
317 
318   return ret;
319 }
320 
print(string & body) const321 void AmSdp::print(string& body) const
322 {
323   string out_buf = "v="+int2str(version)+"\r\n"
324     "o="+origin.user+" "+int2str(origin.sessId)+" "+
325     int2str(origin.sessV)+" IN ";
326 
327   if (!origin.conn.address.empty())
328     if (origin.conn.address.find(".") != std::string::npos)
329       out_buf += "IP4 " + origin.conn.address + "\r\n";
330     else
331       out_buf += "IP6 " + origin.conn.address + "\r\n";
332   else if (!conn.address.empty())
333     if (conn.address.find(".") != std::string::npos)
334       out_buf += "IP4 " + conn.address + "\r\n";
335     else
336       out_buf += "IP6 " + conn.address + "\r\n";
337   else if (media.size() && !media[0].conn.address.empty())
338     if (media[0].conn.address.find(".") != std::string::npos)
339       out_buf += "IP4 " + media[0].conn.address + "\r\n";
340     else
341       out_buf += "IP6 " + media[0].conn.address + "\r\n";
342   else
343     out_buf += "IP4 0.0.0.0\r\n";
344 
345   out_buf +=
346     "s="+sessionName+"\r\n";
347   if (!conn.address.empty()) {
348     if (conn.address.find(".") != std::string::npos)
349       out_buf += "c=IN IP4 ";
350     else
351       out_buf += "c=IN IP6 ";
352     out_buf += conn.address + "\r\n";
353   }
354 
355   out_buf += "t=0 0\r\n";
356 
357   // add attributes (session level)
358   for (std::vector<SdpAttribute>::const_iterator a_it=
359 	 attributes.begin(); a_it != attributes.end(); a_it++) {
360     out_buf += a_it->print();
361   }
362 
363   for(std::vector<SdpMedia>::const_iterator media_it = media.begin();
364       media_it != media.end(); media_it++) {
365 
366       out_buf += "m=" + media_t_2_str(media_it->type) + " " + int2str(media_it->port) + " " + transport_p_2_str(media_it->transport);
367 
368       string options;
369 
370       if (media_it->transport == TP_RTPAVP || media_it->transport == TP_RTPAVPF || media_it->transport == TP_RTPSAVP || media_it->transport == TP_RTPSAVPF || media_it->transport == TP_UDPTLSRTPSAVP || media_it->transport == TP_UDPTLSRTPSAVPF) {
371 	for(std::vector<SdpPayload>::const_iterator pl_it = media_it->payloads.begin();
372 	    pl_it != media_it->payloads.end(); pl_it++) {
373 
374 	  out_buf += " " + int2str(pl_it->payload_type);
375 
376 	  // "a=rtpmap:" line
377 	  if (!pl_it->encoding_name.empty()) {
378 	    options += "a=rtpmap:" + int2str(pl_it->payload_type) + " "
379 	      + pl_it->encoding_name + "/" + int2str(pl_it->clock_rate);
380 
381 	    if(pl_it->encoding_param > 0){
382 	      options += "/" + int2str(pl_it->encoding_param);
383 	    }
384 
385 	    options += "\r\n";
386 	  }
387 
388 	  // "a=fmtp:" line
389 	  if(pl_it->sdp_format_parameters.size()){
390 	    options += "a=fmtp:" + int2str(pl_it->payload_type) + " "
391 	      + pl_it->sdp_format_parameters + "\r\n";
392 	  }
393 
394 	}
395       }
396       else {
397         // for other transports (UDP/UDPTL) just print out fmt
398         out_buf += " " + media_it->fmt;
399         // ... and continue with c=, attributes, ...
400       }
401 
402       if (!media_it->conn.address.empty())
403         out_buf += "\r\nc=IN " + addr_t_2_str(media_it->conn.addrType) +
404 		" " + media_it->conn.address;
405 
406       out_buf += "\r\n" + options;
407 
408       if(media_it->send){
409 	if(media_it->recv){
410 	  out_buf += "a=sendrecv\r\n";
411 	}
412 	else {
413 	  out_buf += "a=sendonly\r\n";
414 	}
415       }
416       else {
417 	if(media_it->recv){
418 	  out_buf += "a=recvonly\r\n";
419 	}
420 	else {
421 	  out_buf += "a=inactive\r\n";
422 	}
423       }
424 
425       // add attributes (media level)
426       for (std::vector<SdpAttribute>::const_iterator a_it=
427 	     media_it->attributes.begin(); a_it != media_it->attributes.end(); a_it++) {
428 	out_buf += a_it->print();
429       }
430 
431       switch (media_it->dir) {
432       case SdpMedia::DirActive:  out_buf += "a=direction:active\r\n"; break;
433       case SdpMedia::DirPassive: out_buf += "a=direction:passive\r\n"; break;
434       case SdpMedia::DirBoth:  out_buf += "a=direction:both\r\n"; break;
435       case SdpMedia::DirUndefined: break;
436       }
437   }
438 
439   body = out_buf;
440 }
441 
telephoneEventPayload() const442 const SdpPayload* AmSdp::telephoneEventPayload() const
443 {
444   return findPayload("telephone-event");
445 }
446 
findPayload(const string & name) const447 const SdpPayload *AmSdp::findPayload(const string& name) const
448 {
449   vector<SdpMedia>::const_iterator m_it;
450 
451   for (m_it = media.begin(); m_it != media.end(); ++m_it)
452     {
453       vector<SdpPayload>::const_iterator it = m_it->payloads.begin();
454       for(; it != m_it->payloads.end(); ++it)
455 
456 	{
457 	  if (it->encoding_name == name)
458 	    {
459 	      return new SdpPayload(*it);
460 	    }
461 	}
462     }
463   return NULL;
464 }
465 
operator ==(const AmSdp & other) const466 bool AmSdp::operator == (const AmSdp& other) const
467 {
468   if (attributes.empty()) {
469     if (!other.attributes.empty())
470       return false;
471 
472   } else if (other.attributes.empty()) {
473     return false;
474 
475   } else {
476     std::pair<vector<SdpAttribute>::const_iterator, vector<SdpAttribute>::const_iterator> a_mismatch
477       = std::mismatch(attributes.begin(), attributes.end(), other.attributes.begin());
478 
479     if (a_mismatch.first != attributes.end() || a_mismatch.second != other.attributes.end())
480       return false;
481   }
482 
483   if (media.empty()) {
484     if (!other.media.empty())
485       return false;
486 
487   } else if (other.media.empty()) {
488     return false;
489 
490   } else {
491     std::pair<vector<SdpMedia>::const_iterator, vector<SdpMedia>::const_iterator> m_mismatch
492       = std::mismatch(media.begin(), media.end(), other.media.begin());
493 
494     if (m_mismatch.first != media.end() || m_mismatch.second != other.media.end())
495       return false;
496   }
497 
498   return version == other.version && origin == other.origin
499     && sessionName == other.sessionName && uri == other.uri && conn == other.conn;
500 }
501 
clear()502 void AmSdp::clear()
503 {
504   version = 0;
505   origin  = SdpOrigin();
506   sessionName.clear();
507   uri.clear();
508   conn = SdpConnection();
509   attributes.clear();
510   media.clear();
511   l_origin = SdpOrigin();
512 }
513 
calcAnswer(const AmPayloadProvider * payload_prov,SdpMedia & answer) const514 void SdpMedia::calcAnswer(const AmPayloadProvider* payload_prov,
515 			  SdpMedia& answer) const
516 {
517   if(!recv) answer.send = false;
518   if(!send) answer.recv = false;
519 
520   switch(dir){
521   case SdpMedia::DirBoth:
522     answer.dir = SdpMedia::DirBoth;
523     break;
524   case SdpMedia::DirActive:
525     answer.dir = SdpMedia::DirPassive;
526     break;
527   case SdpMedia::DirPassive:
528     answer.dir = SdpMedia::DirActive;
529     break;
530   case SdpMedia::DirUndefined:
531     answer.dir = SdpMedia::DirUndefined;
532     break;
533   }
534 
535   // Calculate the intersection with the offered set of payloads
536   vector<SdpPayload>::const_iterator it = payloads.begin();
537   for(; it!= payloads.end(); ++it) {
538     amci_payload_t* a_pl = NULL;
539     if(it->payload_type < DYNAMIC_PAYLOAD_TYPE_START) {
540       // try static payloads
541       a_pl = payload_prov->payload(it->payload_type);
542     }
543 
544     if( a_pl) {
545       answer.payloads.push_back(SdpPayload(a_pl->payload_id,
546 					   a_pl->name,
547 					   a_pl->advertised_sample_rate,
548 					   0));
549     }
550     else {
551       // Try dynamic payloads
552       // and give a chance to broken
553       // implementation using a static payload number
554       // for dynamic ones.
555 
556       int int_pt = payload_prov->
557 	getDynPayload(it->encoding_name,
558 		      it->clock_rate,
559 		      it->encoding_param);
560       if(int_pt != -1){
561 	answer.payloads.push_back(*it);
562       }
563     }
564   }
565 }
566 
567 //parser
parse_sdp_line_ex(AmSdp * sdp_msg,char * & s)568 static bool parse_sdp_line_ex(AmSdp* sdp_msg, char*& s)
569 {
570   if (!s) return true; // SDP can't be empty, return error (true really used for failure?)
571 
572   char* next=0; size_t line_len = 0;
573   register parse_st state;
574   //default state
575   state=SDP_DESCR;
576   DBG("parsing SDP message...\n");
577 
578   while(*s != '\0'){
579     switch(state){
580     case SDP_DESCR:
581       switch(*s){
582       case 'v':
583 	{
584 	  s = is_eql_next(s);
585 	  next = skip_till_next_line(s, line_len);
586 	  if (line_len) {
587 	    string version(s, line_len);
588 	    str2i(version, sdp_msg->version);
589 	    //	    DBG("parse_sdp_line_ex: found version '%s'\n", version.c_str());
590 	  } else {
591 	    sdp_msg->version = 0;
592 	  }
593 	  s = next;
594 	  state = SDP_DESCR;
595 	  break;
596 
597 	}
598       case 'o':
599 	//DBG("parse_sdp_line_ex: found origin\n");
600 	s = is_eql_next(s);
601 	parse_sdp_origin(sdp_msg, s);
602 	s = get_next_line(s);
603 	state = SDP_DESCR;
604 	break;
605       case 's':
606 	{
607 	  s = is_eql_next(s);
608 	  next = skip_till_next_line(s, line_len);
609 	  if (line_len) {
610 	    sdp_msg->sessionName = string(s, line_len);
611 	  } else {
612 	    sdp_msg->sessionName.clear();
613 	  }
614 	  s = next;
615 	  break;
616 	}
617 
618       case 'u': {
619 	  s = is_eql_next(s);
620 	  next = skip_till_next_line(s, line_len);
621 	  if (line_len) {
622 	    sdp_msg->uri = string(s, line_len);
623 	  } else {
624 	    sdp_msg->uri.clear();
625 	  }
626 	  s = next;
627       } break;
628 
629       case 'i':
630       case 'e':
631       case 'p':
632       case 'b':
633       case 't':
634       case 'k':
635 	s = is_eql_next(s);
636 	s = skip_till_next_line(s, line_len);
637 	state = SDP_DESCR;
638 	break;
639       case 'a':
640 	s = is_eql_next(s);
641 	parse_session_attr(sdp_msg, s, &next);
642 	// next = get_next_line(s);
643 	// parse_sdp_attr(sdp_msg, s);
644 	s = next;
645 	state = SDP_DESCR;
646 	break;
647       case 'c':
648 	s = is_eql_next(s);
649 	s = parse_sdp_connection(sdp_msg, s, 'd');
650 	state = SDP_DESCR;
651 	break;
652       case 'm':
653 	//DBG("parse_sdp_line_ex: found media\n");
654 	state = SDP_MEDIA;
655 	break;
656 
657       default:
658 	{
659 	  next = skip_till_next_line(s, line_len);
660 	  if (line_len) {
661 	    sdp_msg->uri = string(s, line_len);
662 	  } else {
663 	    sdp_msg->uri.clear();
664 	  }
665 
666 	  next = skip_till_next_line(s, line_len);
667 	  if (line_len) {
668 	    DBG("parse_sdp_line: skipping unknown Session description %s=\n",
669 		string(s, line_len).c_str());
670 	  }
671 	  s = next;
672 	  break;
673 	}
674       }
675       break;
676 
677     case SDP_MEDIA:
678       switch(*s){
679       case 'm':
680 	s = is_eql_next(s);
681 	parse_sdp_media(sdp_msg, s);
682 	s = skip_till_next_line(s, line_len);
683 	state = SDP_MEDIA;
684 	break;
685       case 'i':
686 	s = is_eql_next(s);
687 	s = skip_till_next_line(s, line_len);
688 	state = SDP_MEDIA;
689 	break;
690       case 'c':
691 	s = is_eql_next(s);
692 	//DBG("parse_sdp_line: found media connection\n");
693 	s = parse_sdp_connection(sdp_msg, s, 'm');
694 	state = SDP_MEDIA;
695 	break;
696       case 'b':
697 	s = is_eql_next(s);
698 	s = skip_till_next_line(s, line_len);
699 	state = SDP_MEDIA;
700 	break;
701       case 'k':
702 	s = is_eql_next(s);
703 	s = skip_till_next_line(s, line_len);
704 	state = SDP_MEDIA;
705 	break;
706       case 'a':
707 	s = is_eql_next(s);
708 	s = parse_sdp_attr(sdp_msg, s);
709 	state = SDP_MEDIA;
710 	break;
711 
712       default :
713 	{
714 	  next = skip_till_next_line(s, line_len);
715 	  if (line_len) {
716 	    DBG("parse_sdp_line: skipping unknown Media description '%.*s'\n",
717 		(int)line_len, s);
718 	  }
719 	  s = next;
720 	  break;
721 	}
722       }
723       break;
724     }
725   }
726 
727   return false;
728 }
729 
730 
parse_sdp_connection(AmSdp * sdp_msg,char * s,char t)731 static char* parse_sdp_connection(AmSdp* sdp_msg, char* s, char t)
732 {
733 
734   char* connection_line=s;
735   char* next=0;
736   char* next_line=0;
737   size_t line_len = 0;
738   int parsing=1;
739 
740   SdpConnection c;
741 
742   next_line = skip_till_next_line(s, line_len);
743   if (line_len <= 7) { // should be at least c=IN IP4 ...
744     DBG("short connection line '%.*s'\n", (int)line_len, s);
745     return next_line;
746   }
747 
748   register sdp_connection_st state;
749   state = NET_TYPE;
750 
751   //DBG("parse_sdp_line_ex: parse_sdp_connection: parsing sdp connection\n");
752 
753   while(parsing){
754     switch(state){
755     case NET_TYPE:
756       //Ignore NET_TYPE since it is always IN, fixme
757       c.network = NT_IN; // fixme
758       connection_line +=3; // fixme
759       state = ADDR_TYPE;
760       break;
761     case ADDR_TYPE:
762       {
763 	string addr_type(connection_line,3);
764 
765 	string addr_type_uc = addr_type;
766 	std::transform(addr_type_uc.begin(), addr_type_uc.end(), addr_type_uc.begin(), toupper);
767 	connection_line +=4;
768 	if(addr_type_uc == "IP4"){
769 	  c.addrType = AT_V4;
770 	  state = IP4;
771 	}else if(addr_type_uc == "IP6"){
772 	  c.addrType = AT_V6;
773 	  state = IP6;
774 	}else{
775 	  DBG("parse_sdp_connection: Unknown addr_type in c-line: '%s'\n", addr_type.c_str());
776 	  c.addrType = AT_NONE;
777 	  parsing = 0; // ???
778 	}
779 	break;
780       }
781     case IP4:
782       {
783 	  if(contains(connection_line, next_line, '/')){
784 	      next = parse_until(s, '/');
785 	      c.address = string(connection_line,int(next-connection_line)-2);
786 	  }else{
787 	    c.address = string(connection_line, line_len-7);
788 	  }
789 	  parsing = 0;
790 	  break;
791       }
792 
793     case IP6:
794       {
795 	  if(contains(connection_line, next_line, '/')){
796 	      next = parse_until(s, '/');
797 	      c.address = string(connection_line, int(next-connection_line)-2);
798 	  }else{
799 	      c.address = string(connection_line, line_len-7);
800 	  }
801 	  parsing = 0;
802 	  break;
803       }
804     }
805   }
806   if(t == 'd') {
807     sdp_msg->conn = c;
808     DBG("SDP: got session level connection: %s\n", c.debugPrint().c_str());
809   } else if(t == 'm'){
810     SdpMedia& media = sdp_msg->media.back();
811     media.conn = c;
812     DBG("SDP: got media level connection: %s\n", c.debugPrint().c_str());
813   }
814 
815   //DBG("parse_sdp_line_ex: parse_sdp_connection: done parsing sdp connection\n");
816   return next_line;
817 }
818 
819 
parse_sdp_media(AmSdp * sdp_msg,char * s)820 static void parse_sdp_media(AmSdp* sdp_msg, char* s)
821 {
822   SdpMedia m;
823 
824   register sdp_media_st state;
825   state = MEDIA;
826   int parsing = 1;
827   char* media_line=s;
828   char* next=0;
829   char* line_end=0;
830   line_end = get_next_line(media_line);
831   SdpPayload payload;
832   unsigned int payload_type;
833 
834   //DBG("parse_sdp_line_ex: parse_sdp_media: parsing media description...\n");
835   m.dir = SdpMedia::DirBoth;
836 
837   while(parsing){
838     switch(state){
839     case MEDIA:
840       {
841       next = parse_until(media_line, ' ');
842       string media;
843       if (next > media_line)
844 	media = string(media_line, int(next-media_line)-1);
845       m.type = media_type(media);
846       if(m.type == MT_NONE) {
847 	ERROR("parse_sdp_media: Unknown media type\n");
848       }
849       media_line = next;
850       state = PORT;
851       break;
852       }
853     case PORT:
854       {
855       next = parse_until(media_line, ' ');
856       //check for multiple ports
857       if(contains(media_line, next, '/')){
858 	//port number
859 	next = parse_until(media_line, '/');
860 	string port;
861 	if (next > media_line)
862 	  port = string(media_line, int(next-media_line)-1);
863 	str2i(port, m.port);
864 	//number of ports
865 	media_line = next;
866 	next = parse_until(media_line, ' ');
867 	string nports;
868 	if (next > media_line)
869 	  nports = string(media_line, int(next-media_line)-1);
870 	str2i(nports, m.nports);
871       }else{
872 	//port number
873 	next = parse_until(media_line, ' ');
874 	string port;
875 	if (next > media_line)
876 	  port = string(media_line, int(next-media_line)-1);
877 	str2i(port, m.port);
878 	media_line = next;
879       }
880       state = PROTO;
881       break;
882       }
883     case PROTO:
884       {
885 	next = parse_until(media_line, ' ');
886 	string proto;
887 	if (next > media_line)
888 	  proto = string(media_line, int(next-media_line)-1);
889 	// if(transport_type(proto) < 0){
890 	//   ERROR("parse_sdp_media: Unknown transport protocol\n");
891 	//   state = FMT;
892 	//   break;
893 	// }
894 	m.transport = transport_type(proto);
895 	if(m.transport == TP_NONE){
896 	  DBG("Unknown transport protocol: %s\n",proto.c_str());
897 	}
898 	media_line = next;
899 	state = FMT;
900 	break;
901       }
902     case FMT:
903       {
904 	if (m.transport == TP_RTPAVP || m.transport  == TP_RTPAVPF || m.transport == TP_RTPSAVP || m.transport == TP_RTPSAVPF || m.transport == TP_UDPTLSRTPSAVP || m.transport == TP_UDPTLSRTPSAVPF) {
905 	  if (contains(media_line, line_end, ' ')) {
906 	    next = parse_until(media_line, ' ');
907 	    string value;
908 	    if (next > media_line)
909 	      value = string(media_line, int(next-media_line)-1);
910 
911 	    if (!value.empty()) {
912 	      payload.type = m.type;
913 	      str2i(value, payload_type);
914 	      payload.payload_type = payload_type;
915 	      m.payloads.push_back(payload);
916 	    }
917 	    media_line = next;
918 	  } else {
919 	    string last_value;
920 	    if (line_end>media_line) {
921 	      if (*line_end == '\0') {
922 		// last line in message
923 		last_value = string(media_line, int(line_end-media_line));
924 	      } else {
925 		last_value = string(media_line, int(line_end-media_line)-1);
926 	      }
927 	    }
928 	    if (!last_value.empty()) {
929 	      payload.type = m.type;
930 	      str2i(last_value, payload_type);
931 	      payload.payload_type = payload_type;
932 	      m.payloads.push_back(payload);
933 	    }
934 	    parsing = 0;
935 	  }
936 	} else {
937 	  line_end--;
938 	  while (line_end > media_line &&
939 		 (*line_end == '\r' || *line_end == '\n'))
940 	    line_end--;
941 	  if (line_end>media_line)
942 	    m.fmt = string(media_line, line_end-media_line+1);
943 	  DBG("set media fmt to '%s'\n", m.fmt.c_str());
944 	  parsing = 0;
945 	}
946       } break;
947     }
948   }
949   sdp_msg->media.push_back(m);
950 
951   DBG("SDP: got media: %s\n", m.debugPrint().c_str());
952   //DBG("parse_sdp_line_ex: parse_sdp_media: done parsing media description \n");
953   return;
954 }
955 
956 // session level attribute
parse_session_attr(AmSdp * sdp_msg,char * s,char ** next)957 static void parse_session_attr(AmSdp* sdp_msg, char* s, char** next) {
958   size_t line_len = 0;
959   *next = skip_till_next_line(s, line_len);
960   if (*next == s) {
961     WARN("premature end of SDP in session attr\n");
962     while (**next != '\0') (*next)++;
963     return;
964   }
965   char* attr_end = *next-1;
966   while (attr_end >= s &&
967 	 ((*attr_end == LF) || (*attr_end == CR)))
968     attr_end--;
969 
970   if (*attr_end == ':') {
971     WARN("incorrect SDP: value attrib without value: '%s'\n",
972 	 string(s, attr_end-s+1).c_str());
973     return;
974   }
975 
976   char* col = parse_until(s, attr_end, ':');
977 
978   if (col == attr_end) {
979     // property attribute
980     sdp_msg->attributes.push_back(SdpAttribute(string(s, attr_end-s+1)));
981     // DBG("got session attribute '%.*s\n", (int)(attr_end-s+1), s);
982   } else {
983     // value attribute
984     sdp_msg->attributes.push_back(SdpAttribute(string(s, col-s-1),
985 					       string(col, attr_end-col+1)));
986     // DBG("got session attribute '%.*s:%.*s'\n", (int)(col-s-1), s, (int)(attr_end-col+1), col);
987   }
988 }
989 
990 // media level attribute
parse_sdp_attr(AmSdp * sdp_msg,char * s)991 static char* parse_sdp_attr(AmSdp* sdp_msg, char* s)
992 {
993   if(sdp_msg->media.empty()){
994     ERROR("While parsing media options: no actual media !\n");
995     return s;
996   }
997 
998   SdpMedia& media = sdp_msg->media.back();
999 
1000   SdpPayload payload;
1001 
1002   register sdp_attr_rtpmap_st rtpmap_st;
1003   register sdp_attr_fmtp_st fmtp_st;
1004   rtpmap_st = TYPE;
1005   fmtp_st = FORMAT;
1006   char* attr_line=s;
1007   char* next=0;
1008   char* line_end=0;
1009   size_t line_len = 0;
1010   int parsing = 1;
1011   line_end = skip_till_next_line(attr_line, line_len);
1012 
1013   unsigned int payload_type, clock_rate, encoding_param = 0;
1014   string encoding_name, params;
1015 
1016   string attr;
1017   if (!contains(attr_line, line_end, ':')) {
1018     attr = string(attr_line, line_len);
1019     attr_check(attr);
1020     parsing = 0;
1021   } else {
1022     next = parse_until(attr_line, ':');
1023     attr = string(attr_line, int(next-attr_line)-1);
1024     attr_line = next;
1025   }
1026 
1027   if(attr == "rtpmap"){
1028     while(parsing){
1029       switch(rtpmap_st){
1030       case TYPE:
1031 	{
1032 	  next = parse_until(attr_line, ' ');
1033 	  string type(attr_line, int(next-attr_line)-1);
1034 	  str2i(type,payload_type);
1035 	  attr_line = next;
1036 	  rtpmap_st = ENC_NAME;
1037 	  break;
1038 	}
1039       case ENC_NAME:
1040 	{
1041 	  if(contains(s, line_end, '/')){
1042 	    next = parse_until(attr_line, '/');
1043 	    string enc_name(attr_line, int(next-attr_line)-1);
1044 	    encoding_name = enc_name;
1045 	    attr_line = next;
1046 	    rtpmap_st = CLK_RATE;
1047 	    break;
1048 	  } else {
1049 	    rtpmap_st = ENC_PARAM;
1050 	    break;
1051 	  }
1052 	}
1053       case CLK_RATE:
1054 	{
1055 	  // check for posible encoding parameters after clock rate
1056 	  if(contains(attr_line, line_end, '/')){
1057 	    next = parse_until(attr_line, '/');
1058 	    string clk_rate(attr_line, int(next-attr_line)-1);
1059 	    str2i(clk_rate, clock_rate);
1060 	    attr_line = next;
1061 	    rtpmap_st = ENC_PARAM;
1062 	    //last line check
1063 	  }else if (*line_end == '\0') {
1064 	    string clk_rate(attr_line, int(line_end-attr_line));
1065 	    str2i(clk_rate, clock_rate);
1066 	    parsing = 0;
1067 	    //more lines to come
1068 	  }else{
1069 	    string clk_rate(attr_line, int(line_end-attr_line)-1);
1070 	    str2i(clk_rate, clock_rate);
1071 	    parsing=0;
1072 	  }
1073 
1074 	  break;
1075 	}
1076       case ENC_PARAM:
1077 	{
1078 	  next = parse_until(attr_line, ' ');
1079 	  if(next < line_end){
1080 	    string value(attr_line, int(next-attr_line)-1);
1081 	    str2i(value, encoding_param);
1082 	    attr_line = next;
1083 	    rtpmap_st = ENC_PARAM;
1084 	  }else{
1085 	    string last_value(attr_line, int(line_end-attr_line)-1);
1086 	    str2i(last_value, encoding_param);
1087 	    parsing = 0;
1088 	  }
1089 	  break;
1090 	}
1091 	break;
1092       }
1093     }
1094 
1095     //DBG("found media attr 'rtpmap' type '%d'\n", payload_type);
1096 
1097     vector<SdpPayload>::iterator pl_it;
1098 
1099     for( pl_it=media.payloads.begin();
1100 	 (pl_it != media.payloads.end())
1101 	   && (pl_it->payload_type != int(payload_type));
1102 	 ++pl_it);
1103 
1104     if(pl_it != media.payloads.end()){
1105       *pl_it = SdpPayload( int(payload_type),
1106 			   encoding_name,
1107 			   int(clock_rate),
1108 			   int(encoding_param));
1109     }
1110 
1111   } else if(attr == "fmtp"){
1112     while(parsing){
1113       switch(fmtp_st){ // fixme
1114       case FORMAT:
1115 	{
1116 	  next = parse_until(attr_line, line_end, ' ');
1117 	  string fmtp_format(attr_line, int(next-attr_line)-1);
1118 	  str2i(fmtp_format, payload_type);
1119 	  attr_line = next;
1120 	  fmtp_st = FORMAT_PARAM;
1121 	  break;
1122 	}
1123       case FORMAT_PARAM:
1124 	{
1125 	  char* param_end = line_end-1;
1126 	  while (is_wsp(*param_end))
1127 	    param_end--;
1128 
1129 	  if(attr_line >= param_end) {
1130 	    DBG("empty param for fmtp. ignore it");
1131 	  } else {
1132 	    params = string(attr_line, param_end-attr_line+1);
1133 	  }
1134 	  parsing = 0;
1135 	}
1136 	break;
1137       }
1138     }
1139 
1140     //DBG("found media attr 'fmtp' for payload '%d': '%s'\n",
1141     //  payload_type, params.c_str());
1142 
1143     vector<SdpPayload>::iterator pl_it;
1144 
1145     for(pl_it=media.payloads.begin();
1146 	(pl_it != media.payloads.end())
1147 	  && (pl_it->payload_type != int(payload_type));
1148 	pl_it++);
1149 
1150     if(pl_it != media.payloads.end())
1151       pl_it->sdp_format_parameters = params;
1152 
1153   } else if (attr == "direction") {
1154     if (parsing) {
1155       size_t dir_len = 0;
1156       next = skip_till_next_line(attr_line, dir_len);
1157       string value(attr_line, dir_len);
1158       if (value == "active") {
1159 	media.dir=SdpMedia::DirActive;
1160 	// DBG("found media attr 'direction' value '%s'\n", (char*)value.c_str());
1161       } else if (value == "passive") {
1162 	media.dir=SdpMedia::DirPassive;
1163 	//DBG("found media attr 'direction' value '%s'\n", (char*)value.c_str());
1164       } else if (attr == "both") {
1165 	media.dir=SdpMedia::DirBoth;
1166 	//DBG("found media attr 'direction' value '%s'\n", (char*)value.c_str());
1167       } else {
1168 	DBG("found unknown value for media attribute 'direction'\n");
1169       }
1170     } else {
1171       DBG("ignoring direction attribute without value\n");
1172     }
1173   } else if (attr == "sendrecv") {
1174     media.send = true;
1175     media.recv = true;
1176   } else if (attr == "sendonly") {
1177     media.send = true;
1178     media.recv = false;
1179   } else if (attr == "recvonly") {
1180     media.send = false;
1181     media.recv = true;
1182   } else if (attr == "inactive") {
1183     media.send = false;
1184     media.recv = false;
1185   } else {
1186     attr_check(attr);
1187     string value;
1188     if (parsing) {
1189       size_t attr_len = 0;
1190       next = skip_till_next_line(attr_line, attr_len);
1191       value = string (attr_line, attr_len);
1192     }
1193 
1194     // if (value.empty()) {
1195     //   DBG("got media attribute '%s'\n", attr.c_str());
1196     // } else {
1197     //   DBG("got media attribute '%s':'%s'\n", attr.c_str(), value.c_str());
1198     // }
1199     media.attributes.push_back(SdpAttribute(attr, value));
1200   }
1201   return line_end;
1202 }
1203 
parse_sdp_origin(AmSdp * sdp_msg,char * s)1204 static void parse_sdp_origin(AmSdp* sdp_msg, char* s)
1205 {
1206   char* origin_line = s;
1207   char* next=0;
1208   char* line_end=0;
1209   size_t line_len=0;
1210   line_end = skip_till_next_line(s, line_len);
1211 
1212   register sdp_origin_st origin_st;
1213   origin_st = USER;
1214   int parsing = 1;
1215 
1216   SdpOrigin origin;
1217 
1218   //DBG("parse_sdp_line_ex: parse_sdp_origin: parsing sdp origin\n");
1219 
1220   while(parsing){
1221     switch(origin_st)
1222       {
1223       case USER:
1224 	{
1225 	  next = parse_until(origin_line, ' ');
1226 	  if(next > line_end){
1227 	    DBG("parse_sdp_origin: ST_USER: Incorrect number of value in o=\n");
1228 	    origin_st = UNICAST_ADDR;
1229 	    break;
1230 	  }
1231 	  string user(origin_line, int(next-origin_line)-1);
1232 	  origin.user = user;
1233 	  origin_line = next;
1234 	  origin_st = ID;
1235 	  break;
1236 	}
1237       case ID:
1238 	{
1239 	  next = parse_until(origin_line, ' ');
1240 	  if(next > line_end){
1241 	    DBG("parse_sdp_origin: ST_ID: Incorrect number of value in o=\n");
1242 	    origin_st = UNICAST_ADDR;
1243 	    break;
1244 	  }
1245 	  string id(origin_line, int(next-origin_line)-1);
1246 	  str2i(id, origin.sessId);
1247 	  origin_line = next;
1248 	  origin_st = VERSION_ST;
1249 	  break;
1250 	}
1251       case VERSION_ST:
1252 	{
1253 	  next = parse_until(origin_line, ' ');
1254 	  if(next > line_end){
1255 	    DBG("parse_sdp_origin: ST_VERSION: Incorrect number of value in o=\n");
1256 	    origin_st = UNICAST_ADDR;
1257 	    break;
1258 	  }
1259 	  string version(origin_line, int(next-origin_line)-1);
1260 	  str2i(version, origin.sessV);
1261 	  origin_line = next;
1262 	  origin_st = NETTYPE;
1263 	  break;
1264 	}
1265       case NETTYPE:
1266 	{
1267 	  next = parse_until(origin_line, ' ');
1268 	  if(next > line_end){
1269 	    DBG("parse_sdp_origin: ST_NETTYPE: Incorrect number of value in o=\n");
1270 	    origin_st = UNICAST_ADDR;
1271 	    break;
1272 	  }
1273 	  string net_type(origin_line, int(next-origin_line)-1);
1274 	  origin.conn.network = NT_IN; // fixme
1275 	  origin_line = next;
1276 	  origin_st = ADDR;
1277 	  break;
1278 	}
1279       case ADDR:
1280 	{
1281        	  next = parse_until(origin_line, ' ');
1282 	  if(next > line_end){
1283 	    DBG("parse_sdp_origin: ST_ADDR: Incorrect number of value in o=\n");
1284 	    origin_st = UNICAST_ADDR;
1285 	    break;
1286 	  }
1287 
1288 	  string addr_type(origin_line, int(next-origin_line)-1);
1289 	  if(addr_type == "IP4"){
1290 	    origin.conn.addrType = AT_V4;
1291 	  }else if(addr_type == "IP6"){
1292 	    origin.conn.addrType = AT_V6;
1293 	  }else{
1294 	    DBG("parse_sdp_connection: Unknown addr_type in o line: '%s'\n", addr_type.c_str());
1295 	    origin.conn.addrType = AT_NONE;
1296 	  }
1297 
1298 	  origin_line = next;
1299 	  origin_st = UNICAST_ADDR;
1300 	  break;
1301 	}
1302       case UNICAST_ADDR:
1303 	{
1304 	  next = parse_until(origin_line, ' ');
1305 	  if (next != origin_line) {
1306 	    //check if line contains more values than allowed
1307 	    if(next > line_end){
1308 	      size_t addr_len = 0;
1309 	      skip_till_next_line(origin_line, addr_len);
1310 	      origin.conn.address = string(origin_line, addr_len);
1311 	    }else{
1312 	      DBG("parse_sdp_origin: 'o=' contains more values than allowed; these values will be ignored\n");
1313 	      origin.conn.address = string(origin_line, int(next-origin_line)-1);
1314 	    }
1315 	  } else {
1316 	    origin.conn.address = "";
1317 	  }
1318 	  parsing = 0;
1319 	  break;
1320 	}
1321       }
1322   }
1323 
1324   sdp_msg->origin = origin;
1325 
1326   //DBG("parse_sdp_line_ex: parse_sdp_origin: done parsing sdp origin\n");
1327   return;
1328 }
1329 
1330 
1331 /*
1332  *HELPER FUNCTIONS
1333  */
1334 
contains(char * s,char * next_line,char c)1335 static bool contains(char* s, char* next_line, char c)
1336 {
1337   char* line=s;
1338   while((line != next_line-1) && (*line)){
1339     if(*line == c)
1340       return true;
1341     line++;
1342   }
1343   return false;
1344 }
1345 
is_wsp(char s)1346 static bool is_wsp(char s) {
1347   return s==' ' || s == '\r' || s == '\n' || s == '\t';
1348 }
1349 
parse_until(char * s,char end)1350 static char* parse_until(char* s, char end)
1351 {
1352   char* line=s;
1353   while(*line && *line != end ){
1354     line++;
1355   }
1356   line++;
1357   return line;
1358 }
1359 
parse_until(char * s,char * end,char c)1360 static char* parse_until(char* s, char* end, char c)
1361 {
1362   char* line=s;
1363   while(line<end && *line && *line != c ){
1364     line++;
1365   }
1366   if (line<end)
1367     line++;
1368   return line;
1369 }
1370 
is_eql_next(char * s)1371 static char* is_eql_next(char* s)
1372 {
1373   char* current_line=s;
1374   if(*(++current_line) != '='){
1375     DBG("parse_sdp_line: expected '=' but found <%c> \n", *current_line);
1376   }
1377   current_line +=1;
1378   return current_line;
1379 }
1380 
get_next_line(char * s)1381 inline char* get_next_line(char* s)
1382 {
1383   char* next_line=s;
1384   //search for next line
1385  while( *next_line != '\0') {
1386     if(*next_line == CR){
1387       next_line++;
1388       if (*next_line == LF) {
1389 	next_line++;
1390 	break;
1391       } else {
1392 	continue;
1393       }
1394     } else if (*next_line == LF){
1395       next_line++;
1396       break;
1397     }
1398     next_line++;
1399  }
1400 
1401   return next_line;
1402 }
1403 
1404 /* skip to 0, CRLF or LF;
1405    @return line_len length of current line
1406    @return start of next line
1407 */
skip_till_next_line(char * s,size_t & line_len)1408 inline char* skip_till_next_line(char* s, size_t& line_len)
1409 {
1410   char* next_line=s;
1411   line_len = 0;
1412 
1413   //search for next line
1414   while( *next_line != '\0') {
1415     if (*next_line == CR) {
1416       next_line++;
1417       if (*next_line == LF) {
1418 	next_line++;
1419 	break;
1420       } else {
1421 	continue;
1422       }
1423     } else if (*next_line == LF){
1424       next_line++;
1425       break;
1426     }
1427     line_len++;
1428     next_line++;
1429  }
1430 
1431   return next_line;
1432 }
1433 
1434 /*
1435  *Check if known media type is used
1436  */
media_type(std::string media)1437 static MediaType media_type(std::string media)
1438 {
1439   if(media == "audio")
1440     return MT_AUDIO;
1441   else if(media == "video")
1442     return MT_VIDEO;
1443   else if(media == "application")
1444     return MT_APPLICATION;
1445   else if(media == "text")
1446     return MT_TEXT;
1447   else if(media == "message")
1448     return MT_MESSAGE;
1449   else if(media == "image")
1450     return MT_IMAGE;
1451   else
1452     return MT_NONE;
1453 }
1454 
transport_type(string transport)1455 static TransProt transport_type(string transport)
1456 {
1457   string transport_uc = transport;
1458   std::transform(transport_uc.begin(), transport_uc.end(), transport_uc.begin(), toupper);
1459 
1460   if(transport_uc == "RTP/AVP")
1461     return TP_RTPAVP;
1462   else if(transport_uc == "RTP/AVPF")
1463     return TP_RTPAVPF;
1464   else if(transport_uc == "UDP")
1465     return TP_UDP;
1466   else if(transport_uc == "RTP/SAVP")
1467     return TP_RTPSAVP;
1468   else if(transport_uc == "RTP/SAVPF")
1469     return TP_RTPSAVPF;
1470   else if(transport_uc == "UDP/TLS/RTP/SAVP")
1471     return TP_UDPTLSRTPSAVP;
1472   else if(transport_uc == "UDP/TLS/RTP/SAVPF")
1473     return TP_UDPTLSRTPSAVPF;
1474   else if(transport_uc == "UDPTL")
1475     return TP_UDPTL;
1476   else
1477     return TP_NONE;
1478 }
1479 
1480 /*
1481 *Check if known attribute name is used
1482 */
attr_check(std::string attr)1483 static bool attr_check(std::string attr)
1484 {
1485   if(attr == "cat")
1486     return true;
1487   else if(attr == "keywds")
1488     return true;
1489   else if(attr == "tool")
1490     return true;
1491   else if(attr == "ptime")
1492     return true;
1493   else if(attr == "maxptime")
1494     return true;
1495   else if(attr == "recvonly")
1496     return true;
1497   else if(attr == "sendrecv")
1498     return true;
1499   else if(attr == "sendonly")
1500     return true;
1501   else if(attr == "inactive")
1502     return true;
1503   else if(attr == "orient")
1504     return true;
1505   else if(attr == "type")
1506     return true;
1507   else if(attr == "charset")
1508     return true;
1509   else if(attr == "sdplang")
1510     return true;
1511   else if(attr == "lang")
1512     return true;
1513   else if(attr == "framerate")
1514     return true;
1515   else if(attr == "quality")
1516     return true;
1517   else if(attr == "both")
1518     return true;
1519   else if(attr == "active")
1520     return true;
1521   else if(attr == "passive")
1522     return true;
1523   else {
1524     DBG("unknown attribute: %s\n", (char*)attr.c_str());
1525     return false;
1526   }
1527 }
1528 
1529