Support historical user IDs

This commit is contained in:
Jonas Platte 2019-12-14 15:58:23 +01:00
parent 32733cf782
commit d19d9c054f
No known key found for this signature in database
GPG Key ID: 7D261D771D915378

View File

@ -35,6 +35,12 @@ pub struct UserId {
localpart: String,
/// The network port of the homeserver.
port: u16,
/// Whether this user id is a historical one.
///
/// A historical user id is one that is not legal per the regular user id rules, but was
/// accepted by previous versions of the spec and thus has to be supported because users with
/// these kinds of ids still exist.
is_historical: bool,
}
impl UserId {
@ -54,6 +60,7 @@ impl UserId {
hostname: host,
localpart: localpart.to_string(),
port,
is_historical: false,
})
}
@ -74,6 +81,13 @@ impl UserId {
pub fn port(&self) -> u16 {
self.port
}
/// Whether this user ID is a historical one, i.e. one that doesn't conform to the latest
/// specification of the user ID grammar but is still accepted because it was previously
/// allowed.
pub fn is_historical(&self) -> bool {
self.is_historical
}
}
impl Display for UserId {
@ -112,10 +126,19 @@ impl<'a> TryFrom<&'a str> for UserId {
let downcased_localpart = localpart.to_lowercase();
// See https://matrix.org/docs/spec/appendices#user-identifiers
if downcased_localpart.bytes().any(|b| match b {
b'0'..=b'9' | b'a'..=b'z' | b'-' | b'.' | b'=' | b'_' | b'/' => false,
_ => true,
}) {
let is_fully_conforming = downcased_localpart.bytes().all(|b| match b {
b'0'..=b'9' | b'a'..=b'z' | b'-' | b'.' | b'=' | b'_' | b'/' => true,
_ => false,
});
// If it's not fully conforming, check if it contains characters that are also disallowed
// for historical user IDs. If there are, return an error.
// See https://matrix.org/docs/spec/appendices#historical-user-ids
if !is_fully_conforming
&& downcased_localpart
.bytes()
.any(|b| b < 0x21 || b == b':' || b > 0x7E)
{
return Err(Error::InvalidCharacters);
}
@ -123,6 +146,7 @@ impl<'a> TryFrom<&'a str> for UserId {
hostname: host,
port,
localpart: downcased_localpart,
is_historical: !is_fully_conforming,
})
}
}
@ -138,12 +162,16 @@ mod tests {
#[test]
fn valid_user_id() {
assert_eq!(
UserId::try_from("@carl:example.com")
.expect("Failed to create UserId.")
.to_string(),
"@carl:example.com"
);
let user_id = UserId::try_from("@carl:example.com").expect("Failed to create UserId.");
assert_eq!(user_id.to_string(), "@carl:example.com");
assert!(!user_id.is_historical());
}
#[test]
fn valid_historical_user_id() {
let user_id = UserId::try_from("@a%b[irc]:example.com").expect("Failed to create UserId.");
assert_eq!(user_id.to_string(), "@a%b[irc]:example.com");
assert!(user_id.is_historical());
}
#[test]
@ -200,18 +228,15 @@ mod tests {
#[test]
fn valid_user_id_with_non_standard_port() {
assert_eq!(
UserId::try_from("@carl:example.com:5000")
.expect("Failed to create UserId.")
.to_string(),
"@carl:example.com:5000"
);
let user_id = UserId::try_from("@carl:example.com:5000").expect("Failed to create UserId.");
assert_eq!(user_id.to_string(), "@carl:example.com:5000");
assert!(!user_id.is_historical());
}
#[test]
fn invalid_characters_in_user_id_localpart() {
assert_eq!(
UserId::try_from("@%%%:example.com").err().unwrap(),
UserId::try_from("@te\nst:example.com").err().unwrap(),
Error::InvalidCharacters
);
}