use std::io::{self, Cursor, Error, ErrorKind};
use crate::{error::{HaProxErr, HaProxRes}, map_error, protocol_raw, return_error};
#[allow(unused)]
pub(crate)
fn is_printable_utf8(u: &str, err_lbl: &str) -> HaProxRes<()>
{
for p in u.chars()
{
if p.is_control() == true || p.is_ascii_whitespace() == true || p.is_ascii_control() == true
{
return_error!(ArgumentEinval,
"non-printable characters found in: '{}' for '{}'", sanitize_str_unicode(u), err_lbl);
}
}
return Ok(());
}
#[inline]
pub(crate)
fn is_printable_ascii_nowp(u: &str, err_lbl: &str) -> HaProxRes<()>
{
return is_printable_ascii(u, err_lbl, false);
}
pub(crate)
fn is_printable_ascii(u: &str, err_lbl: &str, whitesp: bool) -> HaProxRes<()>
{
for p in u.chars()
{
if p.is_control() == true ||
(p.is_ascii_whitespace() == true && whitesp == false) ||
p.is_ascii_control() == true ||
p.is_ascii() == false
{
return_error!(ArgumentEinval,
"non-printable characters found in: '{}' for '{}'", sanitize_str_unicode(u), err_lbl);
}
}
return Ok(());
}
pub(crate)
fn check_printable_ascii_single_wp<'a>(u: &'a str, err_lbl: &str) -> HaProxRes<Option<&'a str>>
{
let mut wpi = 0;
if u.ends_with(protocol_raw::HEADER_V1_EOM) == false
{
return Ok(None);
}
let uw_end = &u[0..u.len()-protocol_raw::HEADER_V1_EOM.len()];
for p in uw_end.chars()
{
if p.is_control() == true ||
p.is_ascii() == false
{
return_error!(MalformedData,
"non-printable characters or non-ascii found in: '{}' for '{}'", sanitize_str_unicode(u), err_lbl);
}
else if p.is_ascii_whitespace() == true
{
if wpi > 0
{
return_error!(MalformedData,
"multiple spaces was found in '{}', for '{}'", sanitize_str_unicode(u), err_lbl);
}
wpi += 1;
}
else if p.is_ascii_control() == true
{
return_error!(MalformedData,
"control chars in sequence in '{}', for '{}'", sanitize_str_unicode(u), err_lbl);
}
else
{
wpi = 0;
}
}
return Ok(Some(uw_end));
}
pub(crate)
fn sanitize_str_unicode(st: &str) -> String
{
let mut out = String::with_capacity(st.len());
for c in st.chars()
{
if c.is_alphanumeric() == true ||
c.is_ascii_punctuation() == true ||
c == ' '
{
out.push(c);
}
else
{
let mut buf = [0_u8; 4];
c.encode_utf8(&mut buf);
let formatted: String =
buf[0..c.len_utf8()].into_iter()
.map(|c| format!("\\x{:02x}", c))
.collect();
out.push_str(&formatted);
}
}
return out;
}
pub
fn map_io_err(err: Error) -> HaProxErr
{
return map_error!(IoError, "{}", err)
}
pub trait ReadExtZeroCopy<'zc>
{
fn borrow_exact(&mut self, len: usize) -> std::io::Result<&'zc [u8]>;
}
impl<'zc> ReadExtZeroCopy<'zc> for Cursor<&'zc [u8]>
{
fn borrow_exact(&mut self, len: usize) -> std::io::Result<&'zc [u8]>
{
let start = self.position() as usize;
if self.get_ref().len() < start+len
{
return Err(
io::Error::new(ErrorKind::UnexpectedEof,
format!("len: {}, req: {}", self.get_ref().len(), start+len))
);
}
let out = &self.get_ref()[start..(start+len)];
self.set_position((start+len) as u64);
return Ok(out);
}
}
#[test]
fn test_mult_wp()
{
assert_eq!(
check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n", "test").is_err(),
true
);
assert_eq!(
check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n", "test"),
Ok(Some("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535"))
);
assert_eq!(
check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535", "test"),
Ok(None)
);
assert_eq!(
check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r", "test"),
Ok(None)
);
assert_eq!(
check_printable_ascii_single_wp("PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n\r\n", "test").is_err(),
true
);
}