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}