sipmsg/headers/
sipuri.rs

1use crate::{
2    common::bnfcore::is_unreserved, common::hostport::HostPort,
3    common::nom_wrappers::from_utf8_nom, common::nom_wrappers::take_while_with_escaped,
4    common::traits::NomParser, errorparse::SipParseError, headers::GenericParams,
5    userinfo::UserInfo,
6};
7use alloc::collections::btree_map::BTreeMap;
8use nom::bytes::complete::{take, take_till, take_until};
9
10use core::str;
11
12#[derive(Copy, Clone, PartialEq, Debug)]
13pub enum RequestUriScheme {
14    SIP,
15    SIPS,
16}
17
18impl RequestUriScheme {
19    pub fn from_bytes(s: &[u8]) -> Result<RequestUriScheme, nom::Err<SipParseError>> {
20        match s {
21            b"sip" => Ok(Self::SIP),
22            b"sips" => Ok(Self::SIPS),
23            _ => sip_parse_error!(101, "Can't parse sipuri scheme"),
24        }
25    }
26}
27
28/// hnv-unreserved  =  "[" / "]" / "/" / "?" / ":" / "+" / "$"
29#[inline]
30fn is_hnv_unreserved_char(c: u8) -> bool {
31    c == b'[' || c == b']' || c == b'/' || c == b'?' || c == b':' || c == b'+' || c == b'$'
32}
33
34#[inline]
35fn is_hnv_char(c: u8) -> bool {
36    is_unreserved(c) || is_hnv_unreserved_char(c)
37}
38
39// header          =  hname "=" hvalue
40// hname           =  1*( hnv-unreserved / unreserved / escaped )
41// hvalue          =  *( hnv-unreserved / unreserved / escaped )
42// headers         =  "?" header *( "&" header )
43pub struct SipUriHeader<'a> {
44    pub name: &'a str,
45    pub value: &'a str,
46}
47
48impl<'a> SipUriHeader<'a> {
49    fn parse_header(input: &[u8]) -> nom::IResult<&[u8], SipUriHeader, SipParseError> {
50        let (input, hname) = take_while_with_escaped(input, is_hnv_char)?;
51        if input.len() == 0 || input[0] != b'=' {
52            let (_, hname_str) = from_utf8_nom(hname)?;
53            return Ok((
54                input,
55                SipUriHeader {
56                    name: hname_str,
57                    value: "",
58                },
59            ));
60        }
61
62        let (input, _) = take(1usize)(input)?; // skip =
63
64        let (input, hvalue) = take_while_with_escaped(input, is_hnv_char)?;
65        let (_, hname_str) = from_utf8_nom(hname)?;
66        let (_, hvalue_str) = from_utf8_nom(hvalue)?;
67        Ok((
68            input,
69            SipUriHeader {
70                name: hname_str,
71                value: hvalue_str,
72            },
73        ))
74    }
75}
76
77impl<'a> NomParser<'a> for SipUriHeader<'a> {
78    type ParseResult = BTreeMap<&'a str, &'a str>;
79
80    // Returns: headers =  "?" header *( "&" header )
81    fn parse(input: &'a [u8]) -> nom::IResult<&[u8], Self::ParseResult, SipParseError> {
82        let (input, c) = take(1usize)(input)?;
83        if c[0] != b'?' {
84            return sip_parse_error!(1, "The first character of headers must be '?'");
85        }
86
87        let mut result = BTreeMap::new();
88        let mut inp2 = input;
89        loop {
90            let (input, sip_uri_header) = SipUriHeader::parse_header(inp2)?;
91            result.insert(sip_uri_header.name, sip_uri_header.value);
92            if input.len() == 0 || input[0] != b'&' {
93                inp2 = input;
94                break;
95            }
96            let (input, _) = take(1usize)(input)?;
97            inp2 = input;
98        }
99
100        Ok((inp2, result))
101    }
102}
103
104// URI  =  SIP-URI / SIPS-URI
105// SIP-URI          =  "sip:" [ userinfo ] hostport
106// uri-parameters [ headers ]
107// SIPS-URI         =  "sips:" [ userinfo ] hostport
108// uri-parameters [ headers ]
109// userinfo         =  ( user / telephone-subscriber ) [ ":" password ] "@"
110// hostport         =  host [ ":" port ]
111/// Its general form, in the case of a SIP URI, is: sip:user:password@host:port;uri-parameters?headers
112#[derive(PartialEq, Debug)]
113pub struct SipUri<'a> {
114    pub scheme: RequestUriScheme,
115    user_info: Option<UserInfo<'a>>,
116    pub hostport: HostPort<'a>,
117    // Temporary use parsing from generic-parameters.rs
118    // TODO make according RFC
119    parameters: Option<GenericParams<'a>>,
120    headers: Option<BTreeMap<&'a str, &'a str>>,
121}
122
123impl<'a> SipUri<'a> {
124    pub fn user_info(&self) -> Option<&UserInfo<'a>> {
125        self.user_info.as_ref()
126    }
127
128    pub fn params(&self) -> Option<&GenericParams<'a>> {
129        self.parameters.as_ref()
130    }
131
132    pub fn headers(&self) -> Option<&BTreeMap<&'a str, &'a str>> {
133        self.headers.as_ref()
134    }
135
136    fn try_parse_params(
137        input: &'a [u8],
138    ) -> nom::IResult<&[u8], Option<GenericParams<'a>>, SipParseError> {
139        if input[0] != b';' {
140            return Ok((input, None));
141        }
142        match GenericParams::parse(input) {
143            Ok((input, params)) => {
144                return Ok((input, Some(params)));
145            }
146            Err(e) => {
147                return Err(e);
148            }
149        }
150    }
151
152    fn try_parse_headers(
153        input: &'a [u8],
154    ) -> nom::IResult<&[u8], Option<BTreeMap<&'a str, &'a str>>, SipParseError> {
155        if input[0] != b'?' {
156            return Ok((input, None));
157        }
158        match SipUriHeader::parse(input) {
159            Ok((input, headers)) => {
160                return Ok((input, Some(headers)));
161            }
162            Err(e) => {
163                return Err(e);
164            }
165        }
166    }
167
168    // This function written not well. So, if you want, you can refactor this function
169    pub fn parse_ext(
170        input: &'a [u8],
171        parse_with_parameters: bool,
172    ) -> nom::IResult<&[u8], SipUri<'a>, SipParseError> {
173        let (input, uri_scheme) = take_until(":")(input)?;
174        let (input_after_scheme, _) = take(1usize)(input)?; // skip ':'
175        let scheme = RequestUriScheme::from_bytes(uri_scheme)?;
176
177        let (right_with_ampersat, before_ampersat) =
178            take_till(|c| c == b'@' || c == b'\n' || c == b',')(input_after_scheme)?;
179        let is_user_info_present = right_with_ampersat.is_empty()
180            || right_with_ampersat[0] == b'\n'
181            || right_with_ampersat[0] == b',';
182        // If right_with_apersat reach '\n' is empty there is no user info
183        let userinfo = if is_user_info_present {
184            None
185        } else {
186            Some(UserInfo::from_bytes(before_ampersat)?)
187        };
188        // if: right_with_apersat is empty we take whole string to further parsing
189        // else: otherwise need to skip userinfo part
190        let input = if is_user_info_present {
191            input_after_scheme
192        } else {
193            &right_with_ampersat[1..] /* skip '@' */
194        };
195
196        let (input, hostport) = HostPort::parse(input)?;
197
198        if !parse_with_parameters {
199            let (input, headers) = if input.is_empty() {
200                (input, None)
201            } else {
202                SipUri::try_parse_headers(input)?
203            };
204
205            return Ok((
206                input,
207                SipUri {
208                    scheme: scheme,
209                    user_info: userinfo,
210                    hostport: hostport,
211                    parameters: None,
212                    headers: headers,
213                },
214            ));
215        }
216
217        let (input, params) = if input.is_empty() {
218            (input, None)
219        } else {
220            SipUri::try_parse_params(input)?
221        };
222
223        let (input, headers) = if input.is_empty() {
224            (input, None)
225        } else {
226            SipUri::try_parse_headers(input)?
227        };
228
229        Ok((
230            input,
231            SipUri {
232                scheme: scheme,
233                user_info: userinfo,
234                hostport: hostport,
235                parameters: params,
236                headers: headers,
237            },
238        ))
239    }
240}
241
242impl<'a> NomParser<'a> for SipUri<'a> {
243    type ParseResult = SipUri<'a>;
244
245    fn parse(input: &'a [u8]) -> nom::IResult<&[u8], Self::ParseResult, SipParseError> {
246        SipUri::parse_ext(input, true)
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253
254    #[test]
255    fn test_sip_uri_parse() {
256        let (rest, sip_uri) =
257            SipUri::parse_ext("sip:192.0.2.254:5061>\r\nblablabla@somm".as_bytes(), true).unwrap();
258        assert_eq!(rest, ">\r\nblablabla@somm".as_bytes());
259        assert_eq!(sip_uri.scheme, RequestUriScheme::SIP);
260        assert_eq!(sip_uri.hostport.host, "192.0.2.254");
261        assert_eq!(sip_uri.hostport.port, Some(5061));
262        /************************************************/
263        let (rest, sip_uri) = SipUri::parse_ext("sip:atlanta.com".as_bytes(), true).unwrap();
264        assert_eq!(rest.len(), 0);
265        assert_eq!(sip_uri.scheme, RequestUriScheme::SIP);
266        assert_eq!(sip_uri.hostport.host, "atlanta.com");
267
268        let (rest, sip_uri) = SipUri::parse_ext("sip:alice@atlanta.com".as_bytes(), true).unwrap();
269        assert_eq!(rest.len(), 0);
270        assert_eq!(sip_uri.scheme, RequestUriScheme::SIP);
271        assert_eq!(sip_uri.user_info().unwrap().value, "alice");
272        assert_eq!(sip_uri.hostport.host, "atlanta.com");
273
274        let (rest, sip_uri) = SipUri::parse_ext(
275            "sip:alice:secretword@atlanta.com;transport=tcp".as_bytes(),
276            true,
277        )
278        .unwrap();
279        assert_eq!(rest.len(), 0);
280        assert_eq!(sip_uri.scheme, RequestUriScheme::SIP);
281        assert_eq!(sip_uri.user_info().unwrap().value, "alice");
282        assert_eq!(sip_uri.user_info().unwrap().password, Some("secretword"));
283        assert_eq!(sip_uri.hostport.host, "atlanta.com");
284        assert_eq!(sip_uri.hostport.port, None);
285        assert_eq!(
286            sip_uri.params().unwrap().get(&"transport"),
287            Some(&Some("tcp"))
288        );
289
290        let (rest, sip_uri) = SipUri::parse_ext(
291            "sip:+1-212-555-1212:1234@gateway.com;user=phone".as_bytes(),
292            true,
293        )
294        .unwrap();
295        assert_eq!(rest.len(), 0);
296        assert_eq!(sip_uri.scheme, RequestUriScheme::SIP);
297        assert_eq!(sip_uri.user_info().unwrap().value, "+1-212-555-1212");
298        assert_eq!(sip_uri.user_info().unwrap().password, Some("1234"));
299        assert_eq!(sip_uri.hostport.host, "gateway.com");
300        assert_eq!(sip_uri.hostport.port, None);
301        assert_eq!(sip_uri.params().unwrap().get(&"user"), Some(&Some("phone")));
302
303        let (rest, sip_uri) = SipUri::parse_ext("sips:1212@gateway.com".as_bytes(), true).unwrap();
304        assert_eq!(rest.len(), 0);
305        assert_eq!(sip_uri.scheme, RequestUriScheme::SIPS);
306        assert_eq!(sip_uri.user_info().unwrap().value, "1212");
307        assert_eq!(sip_uri.hostport.host, "gateway.com");
308
309        let (rest, sip_uri) =
310            SipUri::parse_ext("sip:alice@192.0.2.4:8888".as_bytes(), true).unwrap();
311        assert_eq!(rest.len(), 0);
312        assert_eq!(sip_uri.scheme, RequestUriScheme::SIP);
313        assert_eq!(sip_uri.user_info().unwrap().value, "alice");
314        assert_eq!(sip_uri.hostport.host, "192.0.2.4");
315        assert_eq!(sip_uri.hostport.port, Some(8888));
316
317        let (rest, sip_uri) =
318            SipUri::parse_ext("sip:alice;day=tuesday@atlanta.com".as_bytes(), true).unwrap();
319        assert_eq!(rest.len(), 0);
320        assert_eq!(sip_uri.scheme, RequestUriScheme::SIP);
321        assert_eq!(sip_uri.user_info().unwrap().value, "alice;day=tuesday");
322        assert_eq!(sip_uri.hostport.host, "atlanta.com");
323
324        let (rest, sip_uri) = SipUri::parse_ext(
325            "sips:alice@atlanta.com?subject=project%20x&priority=urgent".as_bytes(),
326            true,
327        )
328        .unwrap();
329        assert_eq!(rest.len(), 0);
330        assert_eq!(
331            sip_uri.headers().unwrap().get(&"subject"),
332            Some(&"project%20x")
333        );
334        assert_eq!(sip_uri.headers().unwrap().get(&"priority"), Some(&"urgent"));
335        assert_eq!(sip_uri.scheme, RequestUriScheme::SIPS);
336        assert_eq!(sip_uri.user_info().unwrap().value, "alice");
337        assert_eq!(sip_uri.hostport.host, "atlanta.com");
338
339        let (rest, sip_uri) = SipUri::parse_ext(
340            "sip:atlanta.com;method=REGISTER?to=alice%40atlanta.com".as_bytes(),
341            true,
342        )
343        .unwrap();
344        assert_eq!(rest.len(), 0);
345        assert_eq!(
346            sip_uri.headers().unwrap().get(&"to"),
347            Some(&"alice%40atlanta.com")
348        );
349        assert_eq!(
350            sip_uri.params().unwrap().get(&"method"),
351            Some(&Some("REGISTER"))
352        );
353        assert_eq!(sip_uri.scheme, RequestUriScheme::SIP);
354        assert_eq!(sip_uri.hostport.host, "atlanta.com");
355        assert_eq!(sip_uri.user_info(), None);
356
357        let (rest, sip_uri) = SipUri::parse_ext(
358            "sips:alice@atlanta.com?subject=project%20x&priority=urgent ;transport=tcp".as_bytes(),
359            false,
360        )
361        .unwrap();
362        //   assert_eq!(rest.len(), 0);
363        assert_eq!(
364            sip_uri.headers().unwrap().get(&"subject"),
365            Some(&"project%20x")
366        );
367        assert_eq!(sip_uri.headers().unwrap().get(&"priority"), Some(&"urgent"));
368        assert_eq!(sip_uri.user_info().unwrap().value, "alice");
369        assert_eq!(sip_uri.scheme, RequestUriScheme::SIPS);
370        assert_eq!(sip_uri.hostport.host, "atlanta.com");
371
372        assert_eq!(sip_uri.params(), None);
373
374        assert_eq!(rest, b" ;transport=tcp");
375    }
376}