Merge branch 'heiko/cardholder-name' into 'main'

Cardholder name format handling belongs in openpgp-card-sequoia

See merge request openpgp-card/openpgp-card!25
This commit is contained in:
Nora Widdecke 2022-10-27 18:08:19 +00:00
commit 678cc30455
2 changed files with 22 additions and 29 deletions

View file

@ -336,11 +336,16 @@ impl<'a> Card<Transaction<'a>> {
self.state.opt.cardholder_related_data() self.state.opt.cardholder_related_data()
} }
// Unicode codepoints are a superset of iso-8859-1 characters
fn latin1_to_string(s: &[u8]) -> String {
s.iter().map(|&c| c as char).collect()
}
/// Get cardholder name as a String (this also normalizes the "<" and "<<" filler chars) /// Get cardholder name as a String (this also normalizes the "<" and "<<" filler chars)
pub fn cardholder_name(&mut self) -> Result<String, Error> { pub fn cardholder_name(&mut self) -> Result<Option<String>, Error> {
let crd = self.state.opt.cardholder_related_data()?; let crd = self.state.opt.cardholder_related_data()?;
if let Some(name) = crd.name() { if let Some(name) = crd.name() {
let name = String::from_utf8_lossy(name).to_string(); let name = Self::latin1_to_string(name);
// re-format name ("last<<first") // re-format name ("last<<first")
let name: Vec<_> = name.split("<<").collect(); let name: Vec<_> = name.split("<<").collect();
@ -349,9 +354,9 @@ impl<'a> Card<Transaction<'a>> {
// replace item separators with spaces // replace item separators with spaces
let name = name.replace('<', " "); let name = name.replace('<', " ");
Ok(name) Ok(Some(name))
} else { } else {
Ok("".to_string()) Ok(None)
} }
} }
@ -555,15 +560,17 @@ impl<'app, 'open> Card<Admin<'app, 'open>> {
impl Card<Admin<'_, '_>> { impl Card<Admin<'_, '_>> {
pub fn set_name(&mut self, name: &str) -> Result<(), Error> { pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
if name.len() >= 40 {
return Err(Error::InternalError("name too long".into()));
}
// All chars must be in ASCII7 // All chars must be in ASCII7
if name.chars().any(|c| !c.is_ascii()) { if name.chars().any(|c| !c.is_ascii()) {
return Err(Error::InternalError("Invalid char in name".into())); return Err(Error::InternalError("Invalid char in name".into()));
}; };
// FIXME: encode spaces and do ordering
if name.len() >= 40 {
return Err(Error::InternalError("name too long".into()));
}
self.card().set_name(name.as_bytes()) self.card().set_name(name.as_bytes())
} }

View file

@ -54,35 +54,21 @@ pub fn print_status(
let version = ai.version().to_be_bytes(); let version = ai.version().to_be_bytes();
output.card_version(format!("{}.{}", version[0], version[1])); output.card_version(format!("{}.{}", version[0], version[1]));
// card / cardholder metadata // Cardholder Name
let crd = card.cardholder_related_data()?; if let Some(name) = card.cardholder_name()? {
if let Some(name) = crd.name() {
// FIXME: decoding as utf8 is wrong (the spec defines this field as latin1 encoded)
let name = String::from_utf8_lossy(name).to_string();
// // This field is silly, maybe ignore it?!
// if let Some(sex) = crd.sex() {
// if sex == Sex::Male {
// print!("Mr. ");
// } else if sex == Sex::Female {
// print!("Mrs. ");
// }
// }
// re-format name ("last<<first")
let name: Vec<_> = name.split("<<").collect();
let name = name.iter().cloned().rev().collect::<Vec<_>>().join(" ");
output.card_holder(name); output.card_holder(name);
} }
// We ignore the Cardholder "Sex" field, because it's silly and mostly unhelpful
// Certificate URL
let url = card.url()?; let url = card.url()?;
if !url.is_empty() { if !url.is_empty() {
output.url(url); output.url(url);
} }
if let Some(lang) = crd.lang() { // Language Preference
if let Some(lang) = card.cardholder_related_data()?.lang() {
for lang in lang { for lang in lang {
output.language_preference(format!("{}", lang)); output.language_preference(format!("{}", lang));
} }