Skip to main content

rsipstack/sip/
uri.rs

1use crate::sip::{Error, Method, Transport};
2use std::convert::TryFrom;
3use std::fmt;
4use std::net::IpAddr;
5use std::str::FromStr;
6
7#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
8pub enum Scheme {
9    #[default]
10    Sip,
11    Sips,
12    Other(String),
13}
14
15impl fmt::Display for Scheme {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        match self {
18            Self::Sip => write!(f, "sip"),
19            Self::Sips => write!(f, "sips"),
20            Self::Other(s) => write!(f, "{}", s),
21        }
22    }
23}
24
25impl FromStr for Scheme {
26    type Err = Error;
27    fn from_str(s: &str) -> Result<Self, Self::Err> {
28        match s.trim() {
29            s if s.eq_ignore_ascii_case("sip") => Ok(Self::Sip),
30            s if s.eq_ignore_ascii_case("sips") => Ok(Self::Sips),
31            s => Ok(Self::Other(s.to_string())),
32        }
33    }
34}
35
36#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
37pub struct Auth {
38    pub user: String,
39    pub password: Option<String>,
40}
41
42impl fmt::Display for Auth {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        match &self.password {
45            Some(pw) => write!(
46                f,
47                "{}:{}",
48                percent_encode_user(&self.user),
49                percent_encode_password(pw)
50            ),
51            None => write!(f, "{}", percent_encode_user(&self.user)),
52        }
53    }
54}
55
56fn percent_encode_component(input: &str, allowed: &[u8]) -> String {
57    let mut out = String::with_capacity(input.len());
58    for byte in input.as_bytes() {
59        if byte.is_ascii_alphanumeric() || allowed.contains(byte) {
60            out.push(char::from(*byte));
61        } else {
62            out.push('%');
63            out.push_str(&format!("{:02X}", byte));
64        }
65    }
66    out
67}
68
69fn percent_encode_user(input: &str) -> String {
70    percent_encode_component(input, b"-_.!~*'()&=+$,;?/")
71}
72
73fn percent_encode_password(input: &str) -> String {
74    percent_encode_component(input, b"-_.!~*'()&=+$,")
75}
76
77impl<S: Into<String>> From<S> for Auth {
78    fn from(s: S) -> Self {
79        let s = s.into();
80        if let Some(idx) = s.find(':') {
81            Auth {
82                user: s[..idx].to_string(),
83                password: Some(s[idx + 1..].to_string()),
84            }
85        } else {
86            Auth {
87                user: s,
88                password: None,
89            }
90        }
91    }
92}
93
94#[derive(Debug, PartialEq, Eq, Clone, Hash)]
95pub enum Host {
96    Domain(Domain),
97    IpAddr(IpAddr),
98}
99
100impl fmt::Display for Host {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        match self {
103            Self::Domain(d) => write!(f, "{}", d),
104            Self::IpAddr(ip @ IpAddr::V6(_)) => write!(f, "[{}]", ip),
105            Self::IpAddr(ip) => write!(f, "{}", ip),
106        }
107    }
108}
109
110impl From<Domain> for Host {
111    fn from(d: Domain) -> Self {
112        Self::Domain(d)
113    }
114}
115
116impl From<IpAddr> for Host {
117    fn from(ip: IpAddr) -> Self {
118        Self::IpAddr(ip)
119    }
120}
121
122impl FromStr for Host {
123    type Err = Error;
124    fn from_str(s: &str) -> Result<Self, Self::Err> {
125        let s = s.trim();
126        if let Ok(ip) = s.parse::<IpAddr>() {
127            Ok(Host::IpAddr(ip))
128        } else {
129            Ok(Host::Domain(Domain::from(s)))
130        }
131    }
132}
133
134impl TryFrom<&str> for Host {
135    type Error = Error;
136    fn try_from(s: &str) -> Result<Self, Self::Error> {
137        s.parse()
138    }
139}
140
141impl TryFrom<Host> for IpAddr {
142    type Error = Error;
143    fn try_from(h: Host) -> Result<Self, Self::Error> {
144        match h {
145            Host::IpAddr(ip) => Ok(ip),
146            Host::Domain(d) => d.0.parse::<IpAddr>().map_err(Into::into),
147        }
148    }
149}
150
151#[derive(Debug, PartialEq, Eq, Clone, Hash)]
152pub struct Domain(pub String);
153
154impl fmt::Display for Domain {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        write!(f, "{}", self.0)
157    }
158}
159
160impl<S: Into<String>> From<S> for Domain {
161    fn from(s: S) -> Self {
162        Self(s.into())
163    }
164}
165
166impl From<Domain> for HostWithPort {
167    fn from(d: Domain) -> Self {
168        HostWithPort {
169            host: Host::Domain(d),
170            port: None,
171        }
172    }
173}
174
175pub use crate::sip::transport::Port;
176#[derive(Debug, PartialEq, Eq, Clone, Hash)]
177pub struct HostWithPort {
178    pub host: Host,
179    pub port: Option<Port>,
180}
181
182impl Default for HostWithPort {
183    fn default() -> Self {
184        Self {
185            host: Host::Domain(Domain::from("localhost")),
186            port: None,
187        }
188    }
189}
190
191impl fmt::Display for HostWithPort {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        match &self.port {
194            Some(port) => write!(f, "{}:{}", self.host, port),
195            None => write!(f, "{}", self.host),
196        }
197    }
198}
199
200impl From<std::net::SocketAddr> for HostWithPort {
201    fn from(sa: std::net::SocketAddr) -> Self {
202        Self {
203            host: Host::IpAddr(sa.ip()),
204            port: Some(Port(sa.port())),
205        }
206    }
207}
208
209impl From<IpAddr> for HostWithPort {
210    fn from(ip: IpAddr) -> Self {
211        Self {
212            host: Host::IpAddr(ip),
213            port: None,
214        }
215    }
216}
217
218impl TryFrom<HostWithPort> for std::net::SocketAddr {
219    type Error = Error;
220    fn try_from(h: HostWithPort) -> Result<Self, Self::Error> {
221        let port = h.port.map(|p| p.0).unwrap_or(5060);
222        match h.host {
223            Host::IpAddr(ip) => Ok(std::net::SocketAddr::new(ip, port)),
224            Host::Domain(d) => {
225                let ip: IpAddr = d.0.parse()?;
226                Ok(std::net::SocketAddr::new(ip, port))
227            }
228        }
229    }
230}
231
232impl TryFrom<&str> for HostWithPort {
233    type Error = Error;
234    fn try_from(s: &str) -> Result<Self, Self::Error> {
235        parse_host_with_port(s.trim())
236    }
237}
238
239impl TryFrom<String> for HostWithPort {
240    type Error = Error;
241    fn try_from(s: String) -> Result<Self, Self::Error> {
242        parse_host_with_port(s.trim())
243    }
244}
245
246fn parse_host_with_port(s: &str) -> Result<HostWithPort, Error> {
247    if s.starts_with('[') {
248        if let Some(close) = s.find(']') {
249            let ip_str = &s[1..close];
250            let ip: IpAddr = ip_str
251                .parse()
252                .map_err(|_| Error::ParseError(format!("invalid IPv6: {}", ip_str)))?;
253            let port = if s.len() > close + 1 && s.as_bytes()[close + 1] == b':' {
254                Some(Port(s[close + 2..].parse()?))
255            } else {
256                None
257            };
258            return Ok(HostWithPort {
259                host: Host::IpAddr(ip),
260                port,
261            });
262        }
263    }
264
265    if let Some(colon_pos) = s.rfind(':') {
266        let after = &s[colon_pos + 1..];
267        if after.chars().all(|c| c.is_ascii_digit()) {
268            let port: u16 = after
269                .parse()
270                .map_err(|_| Error::ParseError(format!("invalid port: {}", after)))?;
271            let host_str = &s[..colon_pos];
272            let host: Host = host_str.parse()?;
273            return Ok(HostWithPort {
274                host,
275                port: Some(Port(port)),
276            });
277        }
278    }
279
280    let host: Host = s.parse()?;
281    Ok(HostWithPort { host, port: None })
282}
283
284#[derive(Debug, PartialEq, Eq, Clone, Hash)]
285pub enum Param {
286    Transport(Transport),
287    User(User),
288    Method(Method),
289    Ttl(Ttl),
290    Maddr(Maddr),
291    Lr,
292    Ob,
293    Rport(Option<u16>),
294    Branch(Branch),
295    Received(Received),
296    Tag(Tag),
297    Expires(Expires),
298    Q(Q),
299    Other(OtherParam, Option<OtherParamValue>),
300}
301
302impl fmt::Display for Param {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        match self {
305            Self::Transport(t) => write!(f, ";transport={}", t),
306            Self::User(u) => write!(f, ";user={}", u),
307            Self::Method(m) => write!(f, ";method={}", m),
308            Self::Ttl(t) => write!(f, ";ttl={}", t),
309            Self::Maddr(m) => write!(f, ";maddr={}", m),
310            Self::Lr => write!(f, ";lr"),
311            Self::Ob => write!(f, ";ob"),
312            Self::Rport(None) => write!(f, ";rport"),
313            Self::Rport(Some(p)) => write!(f, ";rport={}", p),
314            Self::Branch(b) => write!(f, ";branch={}", b),
315            Self::Received(r) => write!(f, ";received={}", r),
316            Self::Tag(t) => write!(f, ";tag={}", t),
317            Self::Expires(e) => write!(f, ";expires={}", e),
318            Self::Q(q) => write!(f, ";q={}", q),
319            Self::Other(name, Some(val)) => write!(f, ";{}={}", name, val),
320            Self::Other(name, None) => write!(f, ";{}", name),
321        }
322    }
323}
324
325impl TryFrom<(&str, Option<&str>)> for Param {
326    type Error = Error;
327    fn try_from((name, value): (&str, Option<&str>)) -> Result<Self, Self::Error> {
328        match (name, value) {
329            (n, Some(v)) if n.eq_ignore_ascii_case("transport") => Ok(Param::Transport(v.parse()?)),
330            (n, Some(v)) if n.eq_ignore_ascii_case("user") => Ok(Param::User(User::new(v))),
331            (n, Some(v)) if n.eq_ignore_ascii_case("method") => Ok(Param::Method(v.parse()?)),
332            (n, Some(v)) if n.eq_ignore_ascii_case("ttl") => Ok(Param::Ttl(Ttl::new(v))),
333            (n, Some(v)) if n.eq_ignore_ascii_case("maddr") => Ok(Param::Maddr(Maddr::new(v))),
334            (n, Some(v)) if n.eq_ignore_ascii_case("branch") => Ok(Param::Branch(Branch::new(v))),
335            (n, Some(v)) if n.eq_ignore_ascii_case("received") => {
336                Ok(Param::Received(Received::new(v)))
337            }
338            (n, Some(v)) if n.eq_ignore_ascii_case("tag") => Ok(Param::Tag(Tag::new(v))),
339            (n, Some(v)) if n.eq_ignore_ascii_case("expires") => {
340                Ok(Param::Expires(Expires::new(v)))
341            }
342            (n, Some(v)) if n.eq_ignore_ascii_case("q") => Ok(Param::Q(Q::new(v))),
343            (n, None) if n.eq_ignore_ascii_case("lr") => Ok(Param::Lr),
344            (n, None) if n.eq_ignore_ascii_case("ob") => Ok(Param::Ob),
345            (n, None) if n.eq_ignore_ascii_case("rport") => Ok(Param::Rport(None)),
346            (n, Some(v)) if n.eq_ignore_ascii_case("rport") => {
347                let port = v
348                    .parse::<u16>()
349                    .map_err(|_| Error::ParseError(format!("invalid rport: {}", v)))?;
350                Ok(Param::Rport(Some(port)))
351            }
352            (n, v) => Ok(Param::Other(
353                OtherParam::new(n),
354                v.map(OtherParamValue::new),
355            )),
356        }
357    }
358}
359
360impl FromStr for Param {
361    type Err = Error;
362    fn from_str(s: &str) -> Result<Self, Self::Err> {
363        let s = s.trim_start_matches(';').trim();
364        if let Some(eq) = s.find('=') {
365            let name = &s[..eq];
366            let value = &s[eq + 1..];
367            Param::try_from((name, Some(value)))
368        } else {
369            Param::try_from((s, None))
370        }
371    }
372}
373
374macro_rules! string_newtype {
375    ($name:ident) => {
376        #[derive(Debug, PartialEq, Eq, Clone, Default, Hash)]
377        pub struct $name(pub String);
378
379        impl $name {
380            pub fn new(s: impl Into<String>) -> Self {
381                Self(s.into())
382            }
383            pub fn value(&self) -> &str {
384                &self.0
385            }
386        }
387
388        impl fmt::Display for $name {
389            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390                write!(f, "{}", self.0)
391            }
392        }
393
394        impl From<String> for $name {
395            fn from(s: String) -> Self {
396                Self(s)
397            }
398        }
399        impl From<&str> for $name {
400            fn from(s: &str) -> Self {
401                Self(s.to_string())
402            }
403        }
404        impl From<$name> for String {
405            fn from(s: $name) -> String {
406                s.0
407            }
408        }
409        impl std::ops::Deref for $name {
410            type Target = str;
411            fn deref(&self) -> &str {
412                &self.0
413            }
414        }
415    };
416}
417
418string_newtype!(Branch);
419string_newtype!(Received);
420string_newtype!(Tag);
421string_newtype!(Expires);
422string_newtype!(Q);
423string_newtype!(User);
424string_newtype!(Ttl);
425string_newtype!(Maddr);
426string_newtype!(OtherParam);
427string_newtype!(OtherParamValue);
428
429impl Received {
430    pub fn parse(&self) -> Result<IpAddr, std::net::AddrParseError> {
431        self.0.parse()
432    }
433}
434
435impl From<Tag> for Param {
436    fn from(t: Tag) -> Self {
437        Param::Tag(t)
438    }
439}
440
441impl From<Branch> for Param {
442    fn from(b: Branch) -> Self {
443        Param::Branch(b)
444    }
445}
446
447impl From<Received> for Param {
448    fn from(r: Received) -> Self {
449        Param::Received(r)
450    }
451}
452
453#[derive(Debug, PartialEq, Eq, Clone, Default, Hash)]
454pub struct Uri {
455    pub scheme: Option<Scheme>,
456    pub auth: Option<Auth>,
457    pub host_with_port: HostWithPort,
458    pub params: Vec<Param>,
459    pub headers: Vec<(String, String)>,
460}
461
462impl fmt::Display for Uri {
463    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464        if let Some(scheme) = &self.scheme {
465            write!(f, "{}:", scheme)?;
466        }
467        if let Some(auth) = &self.auth {
468            write!(f, "{}@", auth)?;
469        }
470        write!(f, "{}", self.host_with_port)?;
471        for param in &self.params {
472            write!(f, "{}", param)?;
473        }
474        if !self.headers.is_empty() {
475            write!(f, "?")?;
476            let parts: Vec<_> = self
477                .headers
478                .iter()
479                .map(|(k, v)| format!("{}={}", k, v))
480                .collect();
481            write!(f, "{}", parts.join("&"))?;
482        }
483        Ok(())
484    }
485}
486
487impl Uri {
488    pub fn user(&self) -> Option<&str> {
489        self.auth.as_ref().map(|a| a.user.as_str())
490    }
491    pub fn host(&self) -> &Host {
492        &self.host_with_port.host
493    }
494}
495
496impl TryFrom<&str> for Uri {
497    type Error = Error;
498    fn try_from(s: &str) -> Result<Self, Self::Error> {
499        parse_uri(s.trim())
500    }
501}
502
503impl From<HostWithPort> for Uri {
504    fn from(hwp: HostWithPort) -> Self {
505        Uri {
506            scheme: Some(Scheme::Sip),
507            auth: None,
508            host_with_port: hwp,
509            params: Vec::new(),
510            headers: Vec::new(),
511        }
512    }
513}
514
515impl TryFrom<String> for Uri {
516    type Error = Error;
517    fn try_from(s: String) -> Result<Self, Self::Error> {
518        parse_uri(s.trim())
519    }
520}
521
522impl FromStr for Uri {
523    type Err = Error;
524    fn from_str(s: &str) -> Result<Self, Self::Err> {
525        parse_uri(s.trim())
526    }
527}
528
529pub fn parse_uri(s: &str) -> Result<Uri, Error> {
530    let s = s.trim();
531    let s = s.trim_start_matches('<');
532    let s = s.trim_end_matches('>');
533    let s = s.trim();
534
535    let (main, hdrs_str) = if let Some(q) = s.find('?') {
536        (&s[..q], Some(&s[q + 1..]))
537    } else {
538        (s, None)
539    };
540
541    let (host_part, params_str) = split_at_first_semicolon(main);
542
543    let (scheme, user_host) = if let Some(colon) = host_part.find(':') {
544        let potential_scheme = &host_part[..colon];
545        if potential_scheme.chars().all(|c| c.is_ascii_alphabetic()) {
546            (
547                Some(potential_scheme.parse::<Scheme>()?),
548                &host_part[colon + 1..],
549            )
550        } else {
551            (None, host_part)
552        }
553    } else {
554        (None, host_part)
555    };
556
557    let (auth, host_str) = if let Some(at) = user_host.rfind('@') {
558        (Some(Auth::from(&user_host[..at])), &user_host[at + 1..])
559    } else {
560        (None, user_host)
561    };
562
563    let host_with_port = parse_host_with_port(host_str)?;
564
565    let params = parse_params(params_str.unwrap_or(""))?;
566
567    let headers = if let Some(h) = hdrs_str {
568        h.split('&')
569            .filter_map(|kv| {
570                let mut parts = kv.splitn(2, '=');
571                let k = parts.next()?.to_string();
572                let v = parts.next().unwrap_or("").to_string();
573                Some((k, v))
574            })
575            .collect()
576    } else {
577        vec![]
578    };
579
580    Ok(Uri {
581        scheme,
582        auth,
583        host_with_port,
584        params,
585        headers,
586    })
587}
588
589fn split_at_first_semicolon(s: &str) -> (&str, Option<&str>) {
590    if let Some(pos) = s.find(';') {
591        (&s[..pos], Some(&s[pos + 1..]))
592    } else {
593        (s, None)
594    }
595}
596
597pub fn parse_params(s: &str) -> Result<Vec<Param>, Error> {
598    if s.is_empty() {
599        return Ok(vec![]);
600    }
601    let mut params = Vec::new();
602    for part in s.split(';') {
603        let part = part.trim();
604        if part.is_empty() {
605            continue;
606        }
607        let param = part.parse::<Param>()?;
608        params.push(param);
609    }
610    Ok(params)
611}
612
613pub type UriWithParams = Uri;
614pub type UriWithParamsList = Vec<Uri>;
615
616pub trait ParamsExt {
617    fn params(&self) -> &[Param];
618    fn params_mut(&mut self) -> &mut Vec<Param>;
619
620    fn tag(&self) -> Option<&str> {
621        self.params().iter().find_map(|p| {
622            if let Param::Tag(t) = p {
623                Some(t.value())
624            } else {
625                None
626            }
627        })
628    }
629    fn branch(&self) -> Option<&str> {
630        self.params().iter().find_map(|p| {
631            if let Param::Branch(b) = p {
632                Some(b.value())
633            } else {
634                None
635            }
636        })
637    }
638    fn transport(&self) -> Option<&Transport> {
639        self.params().iter().find_map(|p| {
640            if let Param::Transport(t) = p {
641                Some(t)
642            } else {
643                None
644            }
645        })
646    }
647    fn received(&self) -> Option<&str> {
648        self.params().iter().find_map(|p| {
649            if let Param::Received(r) = p {
650                Some(r.value())
651            } else {
652                None
653            }
654        })
655    }
656    fn rport(&self) -> Option<Option<u16>> {
657        self.params().iter().find_map(|p| {
658            if let Param::Rport(r) = p {
659                Some(*r)
660            } else {
661                None
662            }
663        })
664    }
665    fn has_lr(&self) -> bool {
666        self.params().iter().any(|p| {
667            matches!(p, Param::Lr)
668                || matches!(p, Param::Other(name, Some(value))
669                    if name.value().eq_ignore_ascii_case("lr")
670                        && value.value().eq_ignore_ascii_case("on"))
671        })
672    }
673    fn expires(&self) -> Option<&str> {
674        self.params().iter().find_map(|p| {
675            if let Param::Expires(e) = p {
676                Some(e.value())
677            } else {
678                None
679            }
680        })
681    }
682    fn q(&self) -> Option<&str> {
683        self.params().iter().find_map(|p| {
684            if let Param::Q(q) = p {
685                Some(q.value())
686            } else {
687                None
688            }
689        })
690    }
691    fn other_param(&self, name: &str) -> Option<&str> {
692        self.params().iter().find_map(|p| {
693            if let Param::Other(n, v) = p {
694                if n.value().eq_ignore_ascii_case(name) {
695                    return Some(v.as_ref().map(|v| v.value()).unwrap_or(""));
696                }
697            }
698            None
699        })
700    }
701
702    fn set_tag(&mut self, tag: impl Into<String>) {
703        let new = Param::Tag(Tag::new(tag));
704        let params = self.params_mut();
705        if let Some(p) = params.iter_mut().find(|p| matches!(p, Param::Tag(_))) {
706            *p = new;
707        } else {
708            params.push(new);
709        }
710    }
711    fn set_branch(&mut self, branch: impl Into<String>) {
712        let new = Param::Branch(Branch::new(branch));
713        let params = self.params_mut();
714        if let Some(p) = params.iter_mut().find(|p| matches!(p, Param::Branch(_))) {
715            *p = new;
716        } else {
717            params.push(new);
718        }
719    }
720    fn set_transport(&mut self, t: Transport) {
721        let new = Param::Transport(t);
722        let params = self.params_mut();
723        if let Some(p) = params.iter_mut().find(|p| matches!(p, Param::Transport(_))) {
724            *p = new;
725        } else {
726            params.push(new);
727        }
728    }
729    fn set_rport(&mut self, port: Option<u16>) {
730        let new = Param::Rport(port);
731        let params = self.params_mut();
732        if let Some(p) = params.iter_mut().find(|p| matches!(p, Param::Rport(_))) {
733            *p = new;
734        } else {
735            params.push(new);
736        }
737    }
738    fn set_expires(&mut self, value: impl Into<String>) {
739        let new = Param::Expires(Expires::new(value));
740        let params = self.params_mut();
741        if let Some(p) = params.iter_mut().find(|p| matches!(p, Param::Expires(_))) {
742            *p = new;
743        } else {
744            params.push(new);
745        }
746    }
747    fn set_other_param(&mut self, name: &str, value: Option<&str>) {
748        let new = Param::Other(OtherParam::new(name), value.map(OtherParamValue::new));
749        let params = self.params_mut();
750        if let Some(p) = params.iter_mut().find(|p| {
751            if let Param::Other(n, _) = p {
752                n.value().eq_ignore_ascii_case(name)
753            } else {
754                false
755            }
756        }) {
757            *p = new;
758        } else {
759            params.push(new);
760        }
761    }
762
763    fn remove_tag(&mut self) {
764        self.params_mut().retain(|p| !matches!(p, Param::Tag(_)));
765    }
766    fn remove_branch(&mut self) {
767        self.params_mut().retain(|p| !matches!(p, Param::Branch(_)));
768    }
769    fn remove_transport(&mut self) {
770        self.params_mut()
771            .retain(|p| !matches!(p, Param::Transport(_)));
772    }
773    fn remove_rport(&mut self) {
774        self.params_mut().retain(|p| !matches!(p, Param::Rport(_)));
775    }
776    fn remove_lr(&mut self) {
777        self.params_mut().retain(|p| !matches!(p, Param::Lr));
778    }
779    fn remove_param(&mut self, name: &str) {
780        self.params_mut().retain(|p| {
781            if let Param::Other(n, _) = p {
782                !n.value().eq_ignore_ascii_case(name)
783            } else {
784                true
785            }
786        });
787    }
788
789    fn pop_tag(&mut self) -> Option<String> {
790        let params = self.params_mut();
791        let pos = params.iter().position(|p| matches!(p, Param::Tag(_)))?;
792        if let Param::Tag(t) = params.remove(pos) {
793            Some(t.0)
794        } else {
795            None
796        }
797    }
798    fn pop_branch(&mut self) -> Option<String> {
799        let params = self.params_mut();
800        let pos = params.iter().position(|p| matches!(p, Param::Branch(_)))?;
801        if let Param::Branch(b) = params.remove(pos) {
802            Some(b.0)
803        } else {
804            None
805        }
806    }
807}
808
809impl ParamsExt for Vec<Param> {
810    fn params(&self) -> &[Param] {
811        self.as_slice()
812    }
813    fn params_mut(&mut self) -> &mut Vec<Param> {
814        self
815    }
816}
817
818impl ParamsExt for Uri {
819    fn params(&self) -> &[Param] {
820        &self.params
821    }
822    fn params_mut(&mut self) -> &mut Vec<Param> {
823        &mut self.params
824    }
825}
826
827#[cfg(test)]
828mod tests {
829    use super::{parse_uri, Host, Param, ParamsExt, Scheme};
830
831    #[test]
832    fn uri_param_value_keeps_colons_and_following_params() {
833        let uri = parse_uri(
834            "sip:82.202.218.130;lr=on;ftag=d4nwJ0jF;du=sip:95.143.188.49:5060;did=893.d6d1",
835        )
836        .unwrap();
837        assert_eq!(
838            uri.to_string(),
839            "sip:82.202.218.130;lr=on;ftag=d4nwJ0jF;du=sip:95.143.188.49:5060;did=893.d6d1"
840        );
841        assert!(uri.params.iter().any(|param| matches!(param, Param::Other(name, Some(value)) if name.value().eq_ignore_ascii_case("du") && value.value() == "sip:95.143.188.49:5060")));
842        assert!(uri.params.iter().any(|param| matches!(param, Param::Other(name, Some(value)) if name.value().eq_ignore_ascii_case("did") && value.value() == "893.d6d1")));
843    }
844
845    #[test]
846    fn uri_preserves_username_with_period() {
847        let uri = parse_uri("sip:alice.smith@restsend.com").unwrap();
848        assert_eq!(uri.auth.unwrap().user, "alice.smith");
849    }
850
851    #[test]
852    fn uri_display_percent_encodes_user_and_password() {
853        let uri = parse_uri("sip:al ice:pa:ss@restsend.com").unwrap();
854        assert_eq!(uri.to_string(), "sip:al%20ice:pa%3Ass@restsend.com");
855    }
856
857    #[test]
858    fn user_param_is_parsed_as_uri_param() {
859        let uri = parse_uri("sip:alice@restsend.com;user=phone").unwrap();
860        assert!(matches!(uri.params.first(), Some(Param::User(value)) if value.value() == "phone"));
861    }
862
863    #[test]
864    fn uri_rport_without_value() {
865        let uri = parse_uri("sip:alice@restsend.com;rport").unwrap();
866        assert!(matches!(uri.params.first(), Some(Param::Rport(None))));
867        assert_eq!(uri.to_string(), "sip:alice@restsend.com;rport");
868    }
869
870    #[test]
871    fn uri_rport_with_value() {
872        let uri = parse_uri("sip:alice@restsend.com;rport=51372").unwrap();
873        assert!(matches!(
874            uri.params.first(),
875            Some(Param::Rport(Some(51372)))
876        ));
877        assert_eq!(uri.to_string(), "sip:alice@restsend.com;rport=51372");
878    }
879
880    #[test]
881    fn uri_rport_with_other_params() {
882        let uri = parse_uri("sip:alice@ua.restsend.com;transport=tcp;rport").unwrap();
883        assert!(uri.params.iter().any(|p| matches!(p, Param::Rport(None))));
884        assert!(uri.params.iter().any(|p| matches!(p, Param::Transport(_))));
885    }
886
887    #[test]
888    fn uri_branch_param() {
889        let uri = parse_uri("sip:proxy.restsend.com;branch=z9hG4bK776asdhds").unwrap();
890        assert!(
891            matches!(uri.params.first(), Some(Param::Branch(b)) if b.value() == "z9hG4bK776asdhds")
892        );
893    }
894
895    #[test]
896    fn uri_lr_flag_param() {
897        let uri = parse_uri("sip:proxy.restsend.com;lr").unwrap();
898        assert!(matches!(uri.params.first(), Some(Param::Lr)));
899        assert_eq!(uri.to_string(), "sip:proxy.restsend.com;lr");
900    }
901
902    #[test]
903    fn uri_lr_on_is_treated_as_loose_route() {
904        let uri = parse_uri("sip:proxy.restsend.com;lr=on").unwrap();
905        assert!(uri.has_lr());
906        assert_eq!(uri.to_string(), "sip:proxy.restsend.com;lr=on");
907    }
908
909    #[test]
910    fn uri_sips_scheme() {
911        let uri = parse_uri("sips:alice@restsend.com").unwrap();
912        assert_eq!(uri.scheme, Some(Scheme::Sips));
913        assert_eq!(uri.to_string(), "sips:alice@restsend.com");
914    }
915
916    #[test]
917    fn uri_ipv4_host() {
918        let uri = parse_uri("sip:bob@192.0.2.4").unwrap();
919        assert!(matches!(uri.host_with_port.host, Host::IpAddr(_)));
920        assert_eq!(uri.to_string(), "sip:bob@192.0.2.4");
921    }
922
923    #[test]
924    fn uri_ipv4_host_with_port() {
925        let uri = parse_uri("sip:alice@192.0.2.1:5060").unwrap();
926        assert!(uri.host_with_port.port.is_some());
927        assert_eq!(uri.to_string(), "sip:alice@192.0.2.1:5060");
928    }
929
930    #[test]
931    fn uri_ipv6_host() {
932        let uri = parse_uri("sip:alice@[2001:db8::1]").unwrap();
933        assert!(matches!(uri.host_with_port.host, Host::IpAddr(_)));
934        assert_eq!(uri.to_string(), "sip:alice@[2001:db8::1]");
935    }
936
937    #[test]
938    fn uri_ipv6_host_with_port() {
939        let uri = parse_uri("sip:alice@[2001:db8::1]:5060").unwrap();
940        assert!(uri.host_with_port.port.is_some());
941        assert_eq!(uri.to_string(), "sip:alice@[2001:db8::1]:5060");
942    }
943
944    #[test]
945    fn uri_transport_tcp_param() {
946        use crate::sip::Transport;
947        let uri = parse_uri("sip:alice@restsend.com;transport=tcp").unwrap();
948        assert!(matches!(
949            uri.params.first(),
950            Some(Param::Transport(Transport::Tcp))
951        ));
952        assert_eq!(uri.to_string(), "sip:alice@restsend.com;transport=TCP");
953    }
954
955    #[test]
956    fn uri_transport_tls_param() {
957        use crate::sip::Transport;
958        let uri = parse_uri("sip:alice@restsend.com;transport=tls").unwrap();
959        assert!(matches!(
960            uri.params.first(),
961            Some(Param::Transport(Transport::Tls))
962        ));
963    }
964
965    #[test]
966    fn uri_roundtrip_complex() {
967        let s = "sip:alice@restsend.com;transport=tcp;tag=1928301774";
968        let uri = parse_uri(s).unwrap();
969        assert_eq!(
970            uri.to_string(),
971            "sip:alice@restsend.com;transport=TCP;tag=1928301774"
972        );
973    }
974
975    #[test]
976    fn uri_host_only_no_user() {
977        let uri = parse_uri("sip:restsend.com").unwrap();
978        assert!(uri.auth.is_none());
979        assert_eq!(uri.host_with_port.host, Host::Domain("restsend.com".into()));
980    }
981
982    #[test]
983    fn uri_with_headers() {
984        let uri = parse_uri("sip:alice@restsend.com?subject=project&priority=urgent").unwrap();
985        assert_eq!(uri.headers.len(), 2);
986        assert_eq!(uri.headers[0], ("subject".into(), "project".into()));
987        assert_eq!(uri.headers[1], ("priority".into(), "urgent".into()));
988    }
989
990    #[test]
991    fn uri_maddr_param() {
992        let uri = parse_uri("sip:alice@restsend.com;maddr=239.255.255.1").unwrap();
993        assert!(
994            matches!(uri.params.first(), Some(Param::Maddr(m)) if m.value() == "239.255.255.1")
995        );
996    }
997
998    #[test]
999    fn uri_ttl_param() {
1000        let uri = parse_uri("sip:alice@restsend.com;ttl=15").unwrap();
1001        assert!(matches!(uri.params.first(), Some(Param::Ttl(t)) if t.value() == "15"));
1002    }
1003
1004    #[test]
1005    fn uri_tag_param() {
1006        let uri = parse_uri("sip:alice@restsend.com;tag=1928301774").unwrap();
1007        assert!(matches!(uri.params.first(), Some(Param::Tag(t)) if t.value() == "1928301774"));
1008    }
1009
1010    #[test]
1011    fn uri_multiple_params() {
1012        let uri = parse_uri("sip:alice@restsend.com;transport=tcp;tag=1928301774;lr").unwrap();
1013        assert_eq!(uri.params.len(), 3);
1014    }
1015
1016    #[test]
1017    fn uri_password_auth() {
1018        let uri = parse_uri("sip:alice:secret@restsend.com").unwrap();
1019        let auth = uri.auth.unwrap();
1020        assert_eq!(auth.user, "alice");
1021        assert_eq!(auth.password, Some("secret".into()));
1022    }
1023
1024    #[test]
1025    fn uri_anonymous() {
1026        let uri = parse_uri("sip:anonymous@anonymous.invalid").unwrap();
1027        assert_eq!(uri.auth.unwrap().user, "anonymous");
1028    }
1029}