use std::io::{self, BufReader};
use super::ReadError;
use super::{StdinGuard, UiCallbacks};
use crate::identity::{IdentityFile, RecipientsAccumulator};
use crate::{pqc::mlkem, Recipient};
use crate::EncryptError;
fn parse_recipient(
_filename: &str,
s: String,
recipients: &mut RecipientsAccumulator,
) -> Result<(), ReadError> {
if let Ok(pk) = s.parse::<mlkem::Recipient>() {
recipients.push(Box::new(pk));
return Ok(());
}
Err(ReadError::NonLevel5Recipient(s))
}
fn read_recipients_list<R: io::BufRead>(
filename: &str,
buf: R,
recipients: &mut RecipientsAccumulator,
) -> Result<(), ReadError> {
for (line_number, line) in buf.lines().enumerate() {
let line = line?;
if line.is_empty() || line.find('#') == Some(0) {
continue;
} else {
match parse_recipient(filename, line, recipients) {
Ok(()) => {}
Err(err @ ReadError::InvalidRecipient(_)) => {
return Err(ReadError::InvalidRecipientsFile {
filename: filename.to_owned(),
line_number: line_number + 1,
});
}
Err(err) => {
return Err(err);
}
}
}
}
Ok(())
}
pub fn read_recipients(
recipient_strings: Vec<String>,
recipients_file_strings: Vec<String>,
identity_strings: Vec<String>,
max_work_factor: Option<u8>,
stdin_guard: &mut StdinGuard,
) -> Result<Vec<Box<dyn Recipient + Send>>, ReadError> {
let mut recipients = RecipientsAccumulator::new();
for arg in recipient_strings {
parse_recipient("", arg, &mut recipients)?;
}
for arg in recipients_file_strings {
let f = stdin_guard.open(arg.clone()).map_err(|e| match e {
ReadError::Io(e) if matches!(e.kind(), io::ErrorKind::NotFound) => {
ReadError::MissingRecipientsFile(arg.clone())
}
_ => e,
})?;
let buf = BufReader::new(f);
read_recipients_list(&arg, buf, &mut recipients)?;
}
for identity_string in identity_strings {
let reader = stdin_guard
.open(identity_string.clone())
.map_err(|e| match e {
ReadError::Io(e) if matches!(e.kind(), io::ErrorKind::NotFound) => {
ReadError::IdentityNotFound(identity_string.clone())
}
other => other,
})?;
let identity_file = IdentityFile::from_buffer(BufReader::new(reader))
.map_err(ReadError::from)?
.with_callbacks(UiCallbacks);
let file_recipients = identity_file.to_recipients().map_err(|e| {
if let EncryptError::EncryptedIdentities(decrypt_err) = e {
ReadError::EncryptedIdentities(decrypt_err)
} else {
ReadError::Io(io::Error::new(io::ErrorKind::Other, e.to_string()))
}
})?;
for recipient in file_recipients {
recipients.push(recipient);
}
}
recipients.build().map_err(|e| match e {
EncryptError::MissingRecipients => ReadError::MissingRecipients,
_ => ReadError::Io(io::Error::new(io::ErrorKind::Other, e.to_string())),
})
}