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;