1#![warn(clippy::all)]
6#![forbid(unsafe_code)]
7
8#[macro_use]
9extern crate log;
10#[cfg(feature = "serialize")]
11#[macro_use]
12extern crate serde_derive;
13#[cfg(feature = "serialize")]
14extern crate serde;
15use std::convert::TryFrom;
16use std::fmt;
17
18#[macro_use]
19pub mod attribute_type;
20pub mod address;
21pub mod anonymizer;
22pub mod error;
23pub mod media_type;
24pub mod network;
25
26use address::{AddressTyped, ExplicitlyTypedAddress};
27use anonymizer::{AnonymizingClone, StatefulSdpAnonymizer};
28use attribute_type::{
29 parse_attribute, SdpAttribute, SdpAttributeRid, SdpAttributeSimulcastVersion, SdpAttributeType,
30 SdpSingleDirection,
31};
32use error::{SdpParserError, SdpParserInternalError};
33use media_type::{
34 parse_media, parse_media_vector, SdpFormatList, SdpMedia, SdpMediaLine, SdpMediaValue,
35 SdpProtocolValue,
36};
37use network::{parse_address_type, parse_network_type};
38
39#[derive(Clone)]
44#[cfg_attr(feature = "serialize", derive(Serialize))]
45#[cfg_attr(feature = "enhanced_debug", derive(Debug))]
46pub enum SdpBandwidth {
47 As(u32),
48 Ct(u32),
49 Tias(u32),
50 Unknown(String, u32),
51}
52
53impl fmt::Display for SdpBandwidth {
54 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55 let (tp_string, value) = match *self {
56 SdpBandwidth::As(ref x) => ("AS", x),
57 SdpBandwidth::Ct(ref x) => ("CT", x),
58 SdpBandwidth::Tias(ref x) => ("TIAS", x),
59 SdpBandwidth::Unknown(ref tp, ref x) => (&tp[..], x),
60 };
61 write!(f, "{tp_string}:{value}")
62 }
63}
64
65#[derive(Clone)]
71#[cfg_attr(feature = "serialize", derive(Serialize))]
72#[cfg_attr(feature = "enhanced_debug", derive(Debug))]
73pub struct SdpConnection {
74 pub address: ExplicitlyTypedAddress,
75 pub ttl: Option<u8>,
76 pub amount: Option<u32>,
77}
78
79impl fmt::Display for SdpConnection {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 self.address.fmt(f)?;
82 write_option_string!(f, "/{}", self.ttl)?;
83 write_option_string!(f, "/{}", self.amount)
84 }
85}
86
87impl AnonymizingClone for SdpConnection {
88 fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
89 let mut masked = self.clone();
90 masked.address = anon.mask_typed_address(&self.address);
91 masked
92 }
93}
94
95#[derive(Clone)]
101#[cfg_attr(feature = "serialize", derive(Serialize))]
102#[cfg_attr(feature = "enhanced_debug", derive(Debug))]
103pub struct SdpOrigin {
104 pub username: String,
105 pub session_id: u64,
106 pub session_version: u64,
107 pub unicast_addr: ExplicitlyTypedAddress,
108}
109
110impl fmt::Display for SdpOrigin {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 write!(
113 f,
114 "{username} {sess_id} {sess_vers} {unicast_addr}",
115 username = self.username,
116 sess_id = self.session_id,
117 sess_vers = self.session_version,
118 unicast_addr = self.unicast_addr
119 )
120 }
121}
122
123impl AnonymizingClone for SdpOrigin {
124 fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
125 let mut masked = self.clone();
126 masked.username = anon.mask_origin_user(&self.username);
127 masked.unicast_addr = anon.mask_typed_address(&masked.unicast_addr);
128 masked
129 }
130}
131
132#[derive(Clone)]
139#[cfg_attr(feature = "serialize", derive(Serialize))]
140#[cfg_attr(feature = "enhanced_debug", derive(Debug))]
141pub struct SdpTiming {
142 pub start: u64,
143 pub stop: u64,
144}
145
146impl fmt::Display for SdpTiming {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 write!(f, "{start} {stop}", start = self.start, stop = self.stop)
149 }
150}
151
152#[cfg_attr(feature = "serialize", derive(Serialize))]
153#[cfg_attr(feature = "enhanced_debug", derive(Debug))]
154pub enum SdpType {
155 Attribute(SdpAttribute),
158 Bandwidth(SdpBandwidth),
159 Connection(SdpConnection),
160 Media(SdpMediaLine),
161 Origin(SdpOrigin),
162 Session(String),
163 Timing(SdpTiming),
164 Version(u64),
165}
166
167#[cfg_attr(feature = "serialize", derive(Serialize))]
168#[cfg_attr(feature = "enhanced_debug", derive(Debug))]
169pub struct SdpLine {
170 pub line_number: usize,
171 pub sdp_type: SdpType,
172 pub text: String,
173}
174
175#[derive(Clone)]
193#[cfg_attr(feature = "serialize", derive(Serialize))]
194#[cfg_attr(feature = "enhanced_debug", derive(Debug))]
195pub struct SdpSession {
196 pub version: u64,
197 pub origin: SdpOrigin,
198 pub session: Option<String>,
199 pub connection: Option<SdpConnection>,
200 pub bandwidth: Vec<SdpBandwidth>,
201 pub timing: Option<SdpTiming>,
202 pub attribute: Vec<SdpAttribute>,
203 pub media: Vec<SdpMedia>,
204 pub warnings: Vec<SdpParserError>, }
213
214impl fmt::Display for SdpSession {
215 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216 write!(
217 f,
218 "v={version}\r\n\
219 o={origin}\r\n\
220 s={session}\r\n\
221 {timing}\
222 {bandwidth}\
223 {connection}\
224 {session_attributes}\
225 {media_sections}",
226 version = self.version,
227 origin = self.origin,
228 session = self.get_session_text(),
229 timing = option_to_string!("t={}\r\n", self.timing),
230 bandwidth = maybe_vector_to_string!("b={}\r\n", self.bandwidth, "\r\nb="),
231 connection = option_to_string!("c={}\r\n", self.connection),
232 session_attributes = maybe_vector_to_string!("a={}\r\n", self.attribute, "\r\na="),
233 media_sections = self.media.iter().map(|s| s.to_string()).collect::<String>(),
234 )
235 }
236}
237
238impl SdpSession {
239 pub fn new(version: u64, origin: SdpOrigin, session: String) -> SdpSession {
240 let session = match session.trim() {
241 s if !s.is_empty() => Some(s.to_owned()),
242 _ => None,
243 };
244 SdpSession {
245 version,
246 origin,
247 session,
248 connection: None,
249 bandwidth: Vec::new(),
250 timing: None,
251 attribute: Vec::new(),
252 media: Vec::new(),
253 warnings: Vec::new(),
254 }
255 }
256
257 pub fn get_version(&self) -> u64 {
258 self.version
259 }
260
261 pub fn get_origin(&self) -> &SdpOrigin {
262 &self.origin
263 }
264
265 pub fn get_session(&self) -> &Option<String> {
266 &self.session
267 }
268
269 pub fn get_session_text(&self) -> &str {
270 if let Some(text) = &self.session {
271 text.as_str()
272 } else {
273 " "
274 }
275 }
276 pub fn get_connection(&self) -> &Option<SdpConnection> {
277 &self.connection
278 }
279
280 pub fn set_connection(&mut self, c: SdpConnection) {
281 self.connection = Some(c)
282 }
283
284 pub fn add_bandwidth(&mut self, b: SdpBandwidth) {
285 self.bandwidth.push(b)
286 }
287
288 pub fn set_timing(&mut self, t: SdpTiming) {
289 self.timing = Some(t)
290 }
291
292 pub fn add_attribute(&mut self, a: SdpAttribute) -> Result<(), SdpParserInternalError> {
293 if !a.allowed_at_session_level() {
294 return Err(SdpParserInternalError::Generic(format!(
295 "{a} not allowed at session level"
296 )));
297 };
298 self.attribute.push(a);
299 Ok(())
300 }
301
302 pub fn extend_media(&mut self, v: Vec<SdpMedia>) {
303 self.media.extend(v)
304 }
305
306 pub fn parse_session_vector(&mut self, lines: &mut Vec<SdpLine>) -> Result<(), SdpParserError> {
307 while !lines.is_empty() {
308 let line = lines.remove(0);
309 match line.sdp_type {
310 SdpType::Attribute(a) => {
311 let _line_number = line.line_number;
312 self.add_attribute(a).map_err(|e: SdpParserInternalError| {
313 SdpParserError::Sequence {
314 message: format!("{e}"),
315 line_number: _line_number,
316 }
317 })?
318 }
319 SdpType::Bandwidth(b) => self.add_bandwidth(b),
320 SdpType::Timing(t) => self.set_timing(t),
321 SdpType::Connection(c) => self.set_connection(c),
322
323 SdpType::Origin(_) | SdpType::Session(_) | SdpType::Version(_) => {
324 return Err(SdpParserError::Sequence {
325 message: "version, origin or session at wrong level".to_string(),
326 line_number: line.line_number,
327 });
328 }
329 SdpType::Media(_) => {
330 return Err(SdpParserError::Sequence {
331 message: "media line not allowed in session parser".to_string(),
332 line_number: line.line_number,
333 });
334 }
335 }
336 }
337 Ok(())
338 }
339
340 pub fn get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute> {
341 self.attribute
342 .iter()
343 .find(|a| SdpAttributeType::from(*a) == t)
344 }
345
346 pub fn add_media(
347 &mut self,
348 media_type: SdpMediaValue,
349 direction: SdpAttribute,
350 port: u32,
351 protocol: SdpProtocolValue,
352 addr: ExplicitlyTypedAddress,
353 ) -> Result<(), SdpParserInternalError> {
354 let mut media = SdpMedia::new(SdpMediaLine {
355 media: media_type,
356 port,
357 port_count: 1,
358 proto: protocol,
359 formats: SdpFormatList::Integers(Vec::new()),
360 });
361
362 media.add_attribute(direction)?;
363
364 media.set_connection(SdpConnection {
365 address: addr,
366 ttl: None,
367 amount: None,
368 });
369
370 self.media.push(media);
371
372 Ok(())
373 }
374}
375
376impl AnonymizingClone for SdpSession {
377 fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
378 let mut masked: SdpSession = SdpSession {
379 version: self.version,
380 session: self.session.clone(),
381 origin: self.origin.masked_clone(anon),
382 connection: self.connection.clone(),
383 timing: self.timing.clone(),
384 bandwidth: self.bandwidth.clone(),
385 attribute: Vec::new(),
386 media: Vec::new(),
387 warnings: Vec::new(),
388 };
389 masked.origin = self.origin.masked_clone(anon);
390 masked.connection = masked.connection.map(|con| con.masked_clone(anon));
391 for i in &self.attribute {
392 masked.attribute.push(i.masked_clone(anon));
393 }
394 masked
395 }
396}
397
398#[allow(clippy::unnecessary_wraps)]
401fn parse_session(value: &str) -> Result<SdpType, SdpParserInternalError> {
402 trace!("session: {}", value);
403 Ok(SdpType::Session(String::from(value)))
404}
405
406fn parse_version(value: &str) -> Result<SdpType, SdpParserInternalError> {
407 let ver = value.parse::<u64>()?;
408 if ver != 0 {
409 return Err(SdpParserInternalError::Generic(format!(
410 "version type contains unsupported value {ver}"
411 )));
412 };
413 trace!("version: {}", ver);
414 Ok(SdpType::Version(ver))
415}
416
417fn parse_origin(value: &str) -> Result<SdpType, SdpParserInternalError> {
418 let mut tokens = value.split_whitespace();
419 let username = match tokens.next() {
420 None => {
421 return Err(SdpParserInternalError::Generic(
422 "Origin type is missing username token".to_string(),
423 ));
424 }
425 Some(x) => x,
426 };
427 let session_id = match tokens.next() {
428 None => {
429 return Err(SdpParserInternalError::Generic(
430 "Origin type is missing session ID token".to_string(),
431 ));
432 }
433 Some(x) => x.parse::<u64>()?,
434 };
435 let session_version = match tokens.next() {
436 None => {
437 return Err(SdpParserInternalError::Generic(
438 "Origin type is missing session version token".to_string(),
439 ));
440 }
441 Some(x) => x.parse::<u64>()?,
442 };
443 match tokens.next() {
444 None => {
445 return Err(SdpParserInternalError::Generic(
446 "Origin type is missing network type token".to_string(),
447 ));
448 }
449 Some(x) => parse_network_type(x)?,
450 };
451 let addrtype = match tokens.next() {
452 None => {
453 return Err(SdpParserInternalError::Generic(
454 "Origin type is missing address type token".to_string(),
455 ));
456 }
457 Some(x) => parse_address_type(x)?,
458 };
459 let unicast_addr = match tokens.next() {
460 None => {
461 return Err(SdpParserInternalError::Generic(
462 "Origin type is missing IP address token".to_string(),
463 ));
464 }
465 Some(x) => ExplicitlyTypedAddress::try_from((addrtype, x))?,
466 };
467 if addrtype != unicast_addr.address_type() {
468 return Err(SdpParserInternalError::Generic(
469 "Origin addrtype does not match address.".to_string(),
470 ));
471 }
472 let o = SdpOrigin {
473 username: String::from(username),
474 session_id,
475 session_version,
476 unicast_addr,
477 };
478 trace!("origin: {}", o);
479 Ok(SdpType::Origin(o))
480}
481
482fn parse_connection(value: &str) -> Result<SdpType, SdpParserInternalError> {
483 let cv: Vec<&str> = value.split_whitespace().collect();
484 if cv.len() != 3 {
485 return Err(SdpParserInternalError::Generic(
486 "connection attribute must have three tokens".to_string(),
487 ));
488 }
489 parse_network_type(cv[0])?;
490 let addrtype = parse_address_type(cv[1])?;
491 let mut ttl = None;
492 let mut amount = None;
493 let mut addr_token = cv[2];
494 if addr_token.find('/').is_some() {
495 let addr_tokens: Vec<&str> = addr_token.split('/').collect();
496 if addr_tokens.len() >= 3 {
497 amount = Some(addr_tokens[2].parse::<u32>()?);
498 }
499 ttl = Some(addr_tokens[1].parse::<u8>()?);
500 addr_token = addr_tokens[0];
501 }
502 let address = ExplicitlyTypedAddress::try_from((addrtype, addr_token))?;
503 let c = SdpConnection {
504 address,
505 ttl,
506 amount,
507 };
508 trace!("connection: {}", c);
509 Ok(SdpType::Connection(c))
510}
511
512fn parse_bandwidth(value: &str) -> Result<SdpType, SdpParserInternalError> {
513 let bv: Vec<&str> = value.split(':').collect();
514 if bv.len() != 2 {
515 return Err(SdpParserInternalError::Generic(
516 "bandwidth attribute must have two tokens".to_string(),
517 ));
518 }
519 let bandwidth = bv[1].parse::<u32>()?;
520 let bw = match bv[0].to_uppercase().as_ref() {
521 "AS" => SdpBandwidth::As(bandwidth),
522 "CT" => SdpBandwidth::Ct(bandwidth),
523 "TIAS" => SdpBandwidth::Tias(bandwidth),
524 _ => SdpBandwidth::Unknown(String::from(bv[0]), bandwidth),
525 };
526 trace!("bandwidth: {}", bw);
527 Ok(SdpType::Bandwidth(bw))
528}
529
530fn parse_timing(value: &str) -> Result<SdpType, SdpParserInternalError> {
531 let tv: Vec<&str> = value.split_whitespace().collect();
532 if tv.len() != 2 {
533 return Err(SdpParserInternalError::Generic(
534 "timing attribute must have two tokens".to_string(),
535 ));
536 }
537 let start = tv[0].parse::<u64>()?;
538 let stop = tv[1].parse::<u64>()?;
539 let t = SdpTiming { start, stop };
540 trace!("timing: {}", t);
541 Ok(SdpType::Timing(t))
542}
543
544pub fn parse_sdp_line(line: &str, line_number: usize) -> Result<SdpLine, SdpParserError> {
545 if line.find('=').is_none() {
546 return Err(SdpParserError::Line {
547 error: SdpParserInternalError::Generic("missing = character in line".to_string()),
548 line: line.to_string(),
549 line_number,
550 });
551 }
552 let mut splitted_line = line.splitn(2, '=');
553 let line_type = match splitted_line.next() {
554 None => {
555 return Err(SdpParserError::Line {
556 error: SdpParserInternalError::Generic("missing type".to_string()),
557 line: line.to_string(),
558 line_number,
559 });
560 }
561 Some(t) => {
562 let trimmed = t.trim();
563 if trimmed.len() > 1 {
564 return Err(SdpParserError::Line {
565 error: SdpParserInternalError::Generic("type too long".to_string()),
566 line: line.to_string(),
567 line_number,
568 });
569 }
570 if trimmed.is_empty() {
571 return Err(SdpParserError::Line {
572 error: SdpParserInternalError::Generic("type is empty".to_string()),
573 line: line.to_string(),
574 line_number,
575 });
576 }
577 trimmed.to_lowercase()
578 }
579 };
580 let (line_value, untrimmed_line_value) = match splitted_line.next() {
581 None => {
582 return Err(SdpParserError::Line {
583 error: SdpParserInternalError::Generic("missing value".to_string()),
584 line: line.to_string(),
585 line_number,
586 });
587 }
588 Some(v) => {
589 let trimmed = v.trim();
590 if trimmed.is_empty() && line_type.as_str() != "s" {
592 return Err(SdpParserError::Line {
593 error: SdpParserInternalError::Generic("value is empty".to_string()),
594 line: line.to_string(),
595 line_number,
596 });
597 }
598 (trimmed, v)
599 }
600 };
601 match line_type.as_ref() {
602 "a" => parse_attribute(line_value),
603 "b" => parse_bandwidth(line_value),
604 "c" => parse_connection(line_value),
605 "e" => Err(SdpParserInternalError::Generic(format!(
606 "unsupported type email: {line_value}"
607 ))),
608 "i" => Err(SdpParserInternalError::Generic(format!(
609 "unsupported type information: {line_value}"
610 ))),
611 "k" => Err(SdpParserInternalError::Generic(format!(
612 "unsupported insecure key exchange: {line_value}"
613 ))),
614 "m" => parse_media(line_value),
615 "o" => parse_origin(line_value),
616 "p" => Err(SdpParserInternalError::Generic(format!(
617 "unsupported type phone: {line_value}"
618 ))),
619 "r" => Err(SdpParserInternalError::Generic(format!(
620 "unsupported type repeat: {line_value}"
621 ))),
622 "s" => parse_session(untrimmed_line_value),
623 "t" => parse_timing(line_value),
624 "u" => Err(SdpParserInternalError::Generic(format!(
625 "unsupported type uri: {line_value}"
626 ))),
627 "v" => parse_version(line_value),
628 "z" => Err(SdpParserInternalError::Generic(format!(
629 "unsupported type zone: {line_value}"
630 ))),
631 _ => Err(SdpParserInternalError::Generic(
632 "unknown sdp type".to_string(),
633 )),
634 }
635 .map(|sdp_type| SdpLine {
636 line_number,
637 sdp_type,
638 text: line.to_owned(),
639 })
640 .map_err(|e| match e {
641 SdpParserInternalError::UnknownAddressType(..)
642 | SdpParserInternalError::AddressTypeMismatch { .. }
643 | SdpParserInternalError::Generic(..)
644 | SdpParserInternalError::Integer(..)
645 | SdpParserInternalError::Float(..)
646 | SdpParserInternalError::Domain(..)
647 | SdpParserInternalError::IpAddress(..) => SdpParserError::Line {
648 error: e,
649 line: line.to_string(),
650 line_number,
651 },
652 SdpParserInternalError::Unsupported(..) => SdpParserError::Unsupported {
653 error: e,
654 line: line.to_string(),
655 line_number,
656 },
657 })
658}
659
660fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> {
661 let make_seq_error = |x: &str| SdpParserError::Sequence {
662 message: x.to_string(),
663 line_number: 0,
664 };
665
666 if session.timing.is_none() {
667 return Err(make_seq_error("Missing timing type at session level"));
668 }
669 let media_cons = &session.media.iter().all(|m| m.get_connection().is_some());
673 if !media_cons && session.get_connection().is_none() {
674 return Err(make_seq_error(
675 "Without connection type at session level all media sections must have connection types",
676 ));
677 }
678
679 if session.get_attribute(SdpAttributeType::Extmap).is_some() {
681 for msection in &session.media {
682 if msection.get_attribute(SdpAttributeType::Extmap).is_some() {
683 return Err(make_seq_error(
684 "Extmap can't be define at session and media level",
685 ));
686 }
687 }
688 }
689
690 for msection in &session.media {
691 if msection
692 .get_attribute(SdpAttributeType::RtcpMuxOnly)
693 .is_some()
694 && msection.get_attribute(SdpAttributeType::RtcpMux).is_none()
695 {
696 return Err(make_seq_error(
697 "rtcp-mux-only media sections must also contain the rtcp-mux attribute",
698 ));
699 }
700
701 let rids: Vec<&SdpAttributeRid> = msection
702 .get_attributes()
703 .iter()
704 .filter_map(|attr| match *attr {
705 SdpAttribute::Rid(ref rid) => Some(rid),
706 _ => None,
707 })
708 .collect();
709 let recv_rids: Vec<&str> = rids
710 .iter()
711 .filter_map(|rid| match rid.direction {
712 SdpSingleDirection::Recv => Some(rid.id.as_str()),
713 _ => None,
714 })
715 .collect();
716 let send_rids: Vec<&str> = rids
717 .iter()
718 .filter_map(|rid| match rid.direction {
719 SdpSingleDirection::Send => Some(rid.id.as_str()),
720 _ => None,
721 })
722 .collect();
723
724 for rid_format in rids.iter().flat_map(|rid| &rid.formats) {
725 match *msection.get_formats() {
726 SdpFormatList::Integers(ref int_fmt) => {
727 if !int_fmt.contains(&(u32::from(*rid_format))) {
728 return Err(make_seq_error(
729 "Rid pts must be declared in the media section",
730 ));
731 }
732 }
733 SdpFormatList::Strings(ref str_fmt) => {
734 if !str_fmt.contains(&rid_format.to_string()) {
735 return Err(make_seq_error(
736 "Rid pts must be declared in the media section",
737 ));
738 }
739 }
740 }
741 }
742
743 if let Some(SdpAttribute::Simulcast(simulcast)) =
744 msection.get_attribute(SdpAttributeType::Simulcast)
745 {
746 let check_defined_rids =
747 |simulcast_version_list: &Vec<SdpAttributeSimulcastVersion>,
748 rid_ids: &[&str]|
749 -> Result<(), SdpParserError> {
750 for simulcast_rid in simulcast_version_list.iter().flat_map(|x| &x.ids) {
751 if !rid_ids.contains(&simulcast_rid.id.as_str()) {
752 return Err(make_seq_error(
753 "Simulcast RIDs must be defined in any rid attribute",
754 ));
755 }
756 }
757 Ok(())
758 };
759
760 check_defined_rids(&simulcast.receive, &recv_rids)?;
761 check_defined_rids(&simulcast.send, &send_rids)?;
762 }
763 }
764
765 Ok(())
766}
767
768fn parse_sdp_vector(lines: &mut Vec<SdpLine>) -> Result<SdpSession, SdpParserError> {
769 if lines.len() < 4 {
770 return Err(SdpParserError::Sequence {
771 message: "SDP neeeds at least 4 lines".to_string(),
772 line_number: 0,
773 });
774 }
775
776 let version = match lines.remove(0).sdp_type {
777 SdpType::Version(v) => v,
778 _ => {
779 return Err(SdpParserError::Sequence {
780 message: "first line needs to be version number".to_string(),
781 line_number: 0,
782 });
783 }
784 };
785 let origin = match lines.remove(0).sdp_type {
786 SdpType::Origin(v) => v,
787 _ => {
788 return Err(SdpParserError::Sequence {
789 message: "second line needs to be origin".to_string(),
790 line_number: 1,
791 });
792 }
793 };
794 let session = match lines.remove(0).sdp_type {
795 SdpType::Session(v) => v,
796 _ => {
797 return Err(SdpParserError::Sequence {
798 message: "third line needs to be session".to_string(),
799 line_number: 2,
800 });
801 }
802 };
803 let mut sdp_session = SdpSession::new(version, origin, session);
804
805 let _media_pos = lines
806 .iter()
807 .position(|l| matches!(l.sdp_type, SdpType::Media(_)));
808
809 match _media_pos {
810 Some(p) => {
811 let mut media: Vec<_> = lines.drain(p..).collect();
812 sdp_session.parse_session_vector(lines)?;
813 sdp_session.extend_media(parse_media_vector(&mut media)?);
814 }
815 None => sdp_session.parse_session_vector(lines)?,
816 };
817
818 sanity_check_sdp_session(&sdp_session)?;
819 Ok(sdp_session)
820}
821
822pub fn parse_sdp(sdp: &str, fail_on_warning: bool) -> Result<SdpSession, SdpParserError> {
823 if sdp.is_empty() {
824 return Err(SdpParserError::Line {
825 error: SdpParserInternalError::Generic("empty SDP".to_string()),
826 line: sdp.to_string(),
827 line_number: 0,
828 });
829 }
830 if sdp.len() < 51 {
832 return Err(SdpParserError::Line {
833 error: SdpParserInternalError::Generic("string too short to be valid SDP".to_string()),
834 line: sdp.to_string(),
835 line_number: 0,
836 });
837 }
838 let lines = sdp.lines();
839 let mut errors: Vec<SdpParserError> = Vec::new();
840 let mut warnings: Vec<SdpParserError> = Vec::new();
841 let mut sdp_lines: Vec<SdpLine> = Vec::new();
842 for (line_number, line) in lines.enumerate() {
843 let stripped_line = line.trim();
844 if stripped_line.is_empty() {
845 continue;
846 }
847 match parse_sdp_line(line, line_number) {
848 Ok(n) => {
849 sdp_lines.push(n);
850 }
851 Err(e) => {
852 match e {
853 SdpParserError::Line {
855 error,
856 line,
857 line_number,
858 } => errors.push(SdpParserError::Line {
859 error,
860 line,
861 line_number,
862 }),
863 SdpParserError::Unsupported {
864 error,
865 line,
866 line_number,
867 } => {
868 warnings.push(SdpParserError::Unsupported {
869 error,
870 line,
871 line_number,
872 });
873 }
874 SdpParserError::Sequence {
875 message,
876 line_number,
877 } => errors.push(SdpParserError::Sequence {
878 message,
879 line_number,
880 }),
881 }
882 }
883 };
884 }
885
886 if fail_on_warning && (!warnings.is_empty()) {
887 return Err(warnings.remove(0));
888 }
889
890 if let Some(e) = errors.pop() {
892 return Err(e);
893 };
894
895 let mut session = parse_sdp_vector(&mut sdp_lines)?;
896 session.warnings = warnings;
897
898 for warning in &session.warnings {
899 warn!("Warning: {}", &warning);
900 }
901
902 Ok(session)
903}
904
905#[cfg(test)]
906#[path = "./lib_tests.rs"]
907mod tests;