diff --git a/src/common/uri/auth.rs b/src/common/uri/auth.rs index e28518d..fddb732 100644 --- a/src/common/uri/auth.rs +++ b/src/common/uri/auth.rs @@ -94,39 +94,58 @@ pub mod tokenizer { { pub fn tokenize(part: T) -> GResult { use nom::{ - bytes::complete::{tag, take_till, take_until}, - combinator::rest, + bytes::complete::{tag, take_while}, + combinator::opt, sequence::tuple, }; - let (rem, (auth, _)) = tuple(( - take_till(|c| c == Into::::into(b'.') || c == Into::::into(b'@')), - tag("@"), - ))(part) - .map_err(|_: GenericNomError<'a, T>| { - TokenizerError::from(("auth user", part)).into() + let (rem, user) = + take_while(grammar_user)(part).map_err(|_: GenericNomError<'a, T>| { + TokenizerError::from(("auth user", part)).into() + })?; + + let (rem, password) = opt(tuple((tag(":"), take_while(grammar_password))))(rem) + .map_err(|_: GenericNomError<'a, T>| { + TokenizerError::from(("auth user password", part)).into() + })?; + + let (rem, _) = tag("@")(rem).map_err(|_: GenericNomError<'a, T>| { + TokenizerError::from(("auth user password at", part)).into() })?; - let (user, password) = match tuple::<_, _, nom::error::VerboseError, _>(( - take_until(":"), - tag(":"), - rest, - ))(auth) - { - Ok((_, (user, _, password))) => (user, Some(password)), - Err(_) => { - //this is not going to ever fail actually, since rest never returns an - //error - let (_, user) = rest(auth).map_err(|_: GenericNomError<'a, T>| { - TokenizerError::from(("auth user (no password)", auth)).into() - })?; - (user, None) - } - }; + let password = password.map(|(_tag, password)| password); Ok((rem, Tokenizer::from((user, password)))) } } + + fn grammar_user>(c: I) -> bool { + grammar_unreserved(&c) || grammar_escaped(&c) || grammar_user_unreserved(&c) + } + + fn grammar_password>(c: I) -> bool { + grammar_unreserved(&c) || grammar_escaped(&c) || grammar_password_unreserved(&c) + } + + fn grammar_unreserved>(c: &I) -> bool { + b"-_.!~*'()".iter().any(|mark| &I::from(*mark) == c) || c.clone().is_alphanum() + } + + fn grammar_escaped>(c: &I) -> bool { + &I::from(b'%') == c + } + + fn grammar_user_unreserved>(c: &I) -> bool { + b"&=+$,;?/" + .iter() + .any(|user_unreserved| &I::from(*user_unreserved) == c) + } + + fn grammar_password_unreserved>(c: &I) -> bool { + b"&=+$," + .iter() + .any(|password_unreserved| &I::from(*password_unreserved) == c) + } } #[cfg(feature = "test-utils")] diff --git a/tests/common/uri/auth.rs b/tests/common/uri/auth.rs index b268230..a30dde2 100644 --- a/tests/common/uri/auth.rs +++ b/tests/common/uri/auth.rs @@ -71,7 +71,7 @@ mod tokenizer { assert_eq!( Tokenizer::tokenize("server2.com something".as_bytes()), Err(nom::Err::Error(rsip::TokenizerError::from( - "failed to tokenize auth user: server2.com something" + "failed to tokenize auth user password at: server2.com something" ))), ); } diff --git a/tests/common/uri/uri_with_params_list.rs b/tests/common/uri/uri_with_params_list.rs index bc56aee..cf34f61 100644 --- a/tests/common/uri/uri_with_params_list.rs +++ b/tests/common/uri/uri_with_params_list.rs @@ -104,4 +104,110 @@ mod tokenizer { )), ); } + + #[test] + fn tokenizer2_u8() { + assert_eq!( + Tokenizer::tokenize( + concat!( + "sips:client.biloxi.example.com:5061;maddr=255.255.255.0;foo=192.0.2.201;lr", + ",", + ";level=low" + ) + .as_bytes() + ), + Ok(( + "".as_bytes(), + Tokenizer { + values: vec![ + uri_with_params::Tokenizer { + uri: uri::Tokenizer { + scheme: Some("sips".as_bytes().into()), + auth: None, + host_with_port: ( + "client.biloxi.example.com".as_bytes(), + Some("5061".as_bytes()) + ) + .into(), + params: vec![], + headers: None, + ..Default::default() + }, + params: vec![ + ("maddr".as_bytes(), Some("255.255.255.0".as_bytes())).into(), + ("foo".as_bytes(), Some("192.0.2.201".as_bytes())).into(), + ("lr".as_bytes(), None).into() + ], + ..Default::default() + }, + uri_with_params::Tokenizer { + uri: uri::Tokenizer { + scheme: Some("sip".as_bytes().into()), + auth: Some(("alice.smith".as_bytes(), None).into()), + host_with_port: ("atlanta.example.com".as_bytes(), None).into(), + params: vec![("s".as_bytes(), Some("2".as_bytes())).into()], + headers: None, + ..Default::default() + }, + params: vec![("level".as_bytes(), Some("low".as_bytes())).into()], + ..Default::default() + } + ], + ..Default::default() + } + )), + ); + } + + #[test] + fn tokenizer3_u8() { + assert_eq!( + Tokenizer::tokenize( + concat!( + "sips:localhost:5061;lr", + ",", + ";level=low" + ) + .as_bytes() + ), + Ok(( + "".as_bytes(), + Tokenizer { + values: vec![ + uri_with_params::Tokenizer { + uri: uri::Tokenizer { + scheme: Some("sips".as_bytes().into()), + auth: None, + host_with_port: ( + "localhost".as_bytes(), + Some("5061".as_bytes()) + ) + .into(), + params: vec![], + headers: None, + ..Default::default() + }, + params: vec![ + ("lr".as_bytes(), None).into() + ], + ..Default::default() + }, + uri_with_params::Tokenizer { + uri: uri::Tokenizer { + scheme: Some("sip".as_bytes().into()), + auth: Some(("alice".as_bytes(), None).into()), + host_with_port: ("atlanta.example.com".as_bytes(), None).into(), + params: vec![("s".as_bytes(), Some("2".as_bytes())).into()], + headers: None, + ..Default::default() + }, + params: vec![("level".as_bytes(), Some("low".as_bytes())).into()], + ..Default::default() + } + ], + ..Default::default() + } + )), + ); + } }