use std::{
borrow::Cow,
};
use crate::{
packet::Header,
};
#[allow(clippy::single_match)]
pub fn base64_filter(mut bytes: Cow<[u8]>, base64_data_max: usize,
mut prefix_remaining: usize, prefix_len: usize)
-> (Cow<[u8]>, usize, usize)
{
let mut leading_whitespace = 0;
let base64_data_max = base64_data_max / 4 * 4;
let mut base64_len = 0;
let mut unfiltered_offset = 0;
let mut unfiltered_complete_len = 0;
let mut padding = 0;
while unfiltered_offset < bytes.len()
&& base64_len < base64_data_max
&& ! (padding > 0 && base64_len % 4 == 0)
{
if prefix_remaining > 0 {
prefix_remaining -= 1;
if unfiltered_offset == 0 {
match bytes {
Cow::Borrowed(s) => {
bytes = Cow::Borrowed(&s[1..]);
leading_whitespace += 1;
continue;
}
Cow::Owned(_) => (),
}
}
unfiltered_offset += 1;
continue;
}
match bytes[unfiltered_offset] {
c if c.is_ascii_whitespace() => {
if c == b'\n' {
prefix_remaining = prefix_len;
}
if unfiltered_offset == 0 {
match bytes {
Cow::Borrowed(s) => {
bytes = Cow::Borrowed(&s[1..]);
leading_whitespace += 1;
continue;
}
Cow::Owned(_) => (),
}
}
}
b'=' => {
if padding == 2 {
break;
}
if base64_len % 4 == 0 {
break;
}
if unfiltered_offset != base64_len {
bytes.to_mut()[base64_len] = b'=';
}
base64_len += 1;
if base64_len % 4 == 0 {
unfiltered_complete_len = unfiltered_offset + 1;
}
padding += 1;
}
_ if padding > 0 => break,
b if is_base64_char(&b) => {
if unfiltered_offset != base64_len {
bytes.to_mut()[base64_len] = b;
}
base64_len += 1;
if base64_len % 4 == 0 {
unfiltered_complete_len = unfiltered_offset + 1;
}
}
_ => break,
}
unfiltered_offset += 1;
}
let base64_len = base64_len - (base64_len % 4);
unfiltered_complete_len += leading_whitespace;
match bytes {
Cow::Borrowed(s) =>
(Cow::Borrowed(&s[..base64_len]), unfiltered_complete_len,
prefix_remaining),
Cow::Owned(mut v) => {
crate::vec_truncate(&mut v, base64_len);
(Cow::Owned(v), unfiltered_complete_len, prefix_remaining)
}
}
}
pub fn is_armored_pgp_blob(bytes: &[u8]) -> bool {
let (bytes, _, _) = base64_filter(Cow::Borrowed(bytes), 32, 0, 0);
match base64::decode_config(&bytes, base64::STANDARD) {
Ok(d) => {
if d.is_empty() {
false
} else {
let mut br = buffered_reader::Memory::new(&d);
if let Ok(header) = Header::parse(&mut br) {
header.ctb().tag().valid_start_of_message()
&& header.valid(false).is_ok()
} else {
false
}
}
},
Err(_err) => false,
}
}
pub fn is_base64_char(b: &u8) -> bool {
b.is_ascii_alphanumeric() || *b == b'+' || *b == b'/'
}
pub fn base64_size(s: usize) -> usize {
(s + 3 - 1) / 3 * 4
}
#[test]
fn base64_size_test() {
assert_eq!(base64_size(0), 0);
assert_eq!(base64_size(1), 4);
assert_eq!(base64_size(2), 4);
assert_eq!(base64_size(3), 4);
assert_eq!(base64_size(4), 8);
assert_eq!(base64_size(5), 8);
assert_eq!(base64_size(6), 8);
assert_eq!(base64_size(7), 12);
}