Support IDN
This commit is contained in:
parent
e6b20d7ef5
commit
d2bd46462c
3 changed files with 25 additions and 4 deletions
|
@ -12,4 +12,5 @@ anyhow = { version = "1.0.71", default-features = false, features = ["std"] }
|
|||
clap = { version = "4.3.11", default-features = false, features = ["derive", "std"] }
|
||||
data-encoding = { version = "2.4.0", default-features = false, features = ["std"] }
|
||||
hmac = { version = "0.12.1", default-features = false }
|
||||
idna = { version = "0.4.0", default-features = false, features = ["std"] }
|
||||
sha2 = { version = "0.10.7", default-features = false, features = ["std", "asm"] }
|
||||
|
|
|
@ -113,6 +113,10 @@ No, this project is based on the filter API used by OpenSMTPD.
|
|||
|
||||
However, if someone implemented it in the exact same way for any other MTA, the progressive web app should work.
|
||||
|
||||
### Does it supports IDN?
|
||||
|
||||
Yes, internationalized domain names (IDN) are supported. You can specify domain names either using valid UTF-8 or Punycode ([RFC 3492](https://datatracker.ietf.org/doc/html/rfc3492)).
|
||||
|
||||
### What about key rotation?
|
||||
|
||||
Rotating the key would mean that all previously generated addresses for this local part would suddenly be invalid. Therefore, the key associated with a local part must not change.
|
||||
|
|
|
@ -17,9 +17,10 @@ impl CodedAddress {
|
|||
pub fn parse(s: &str, separator: char) -> Result<Self> {
|
||||
let (local_part, domain) = split_local_part(s);
|
||||
ensure!(!local_part.is_empty(), "{s}: local part cannot be empty");
|
||||
if let Some(dom) = &domain {
|
||||
let domain = domain.map(|dom| -> Result<String> {
|
||||
ensure!(!dom.is_empty(), "{s}: domain cannot be empty");
|
||||
}
|
||||
Ok(idna::domain_to_ascii(&dom)?)
|
||||
}).transpose()?;
|
||||
let parts: Vec<&str> = local_part.split(separator).collect();
|
||||
let local_part = parts[0].to_string();
|
||||
ensure!(!local_part.is_empty(), "{s}: local part cannot be empty");
|
||||
|
@ -108,9 +109,10 @@ impl FromStr for KeyedAddress {
|
|||
let (address, key_b64) = ksplit.unwrap();
|
||||
let (local_part, domain) = split_local_part(address);
|
||||
ensure!(!local_part.is_empty(), "{s}: local part cannot be empty");
|
||||
if let Some(dom) = &domain {
|
||||
let domain = domain.map(|dom| -> Result<String> {
|
||||
ensure!(!dom.is_empty(), "{s}: domain cannot be empty");
|
||||
}
|
||||
Ok(idna::domain_to_ascii(&dom)?)
|
||||
}).transpose()?;
|
||||
let key = BASE64.decode(key_b64.as_bytes())?;
|
||||
ensure!(!key.is_empty(), "{s}: key cannot be empty");
|
||||
Ok(Self {
|
||||
|
@ -346,6 +348,20 @@ mod tests {
|
|||
assert_eq!(addr_1, addr_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_addr_types_idna_1() {
|
||||
let addr_1 = KeyedAddress::from_str("test@mél.example.org:3d74YQqk").unwrap();
|
||||
let addr_2 = CodedAddress::parse("test@xn--ml-bja.example.org", '+').unwrap();
|
||||
assert_eq!(addr_1, addr_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_addr_types_idna_2() {
|
||||
let addr_1 = KeyedAddress::from_str("test@xn--ml-bja.example.org:3d74YQqk").unwrap();
|
||||
let addr_2 = CodedAddress::parse("test@mél.example.org", '+').unwrap();
|
||||
assert_eq!(addr_1, addr_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_addr_types_with_domain_ne() {
|
||||
let addr_1 = KeyedAddress::from_str("test@example.org:3d74YQqk").unwrap();
|
||||
|
|
Loading…
Reference in a new issue