use std::io;
use colored::Colorize;
use sha2::Digest;
use crate::db::UserData;
pub fn print_allow<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
let data = UserData::from_read(r)?;
let mut list = Vec::with_capacity(data.allow.len());
list.extend(data.allow.iter().map(|v| v.as_bytes()));
list.sort();
writeln!(stdout, "\t{}", "Allow List".yellow().bold().underline())?;
for i in list {
stdout.write_all(i)?;
stdout.write_all(b"\n")?;
}
stdout.flush()
}
pub fn print_block<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
let data = UserData::from_read(r)?;
let mut list = Vec::with_capacity(data.block.len());
list.extend(data.block.iter().map(|v| v.as_bytes()));
list.sort();
writeln!(stdout, "\t{}", "Block List".yellow().bold().underline())?;
for i in list {
stdout.write_all(i)?;
stdout.write_all(b"\n")?;
}
stdout.flush()
}
pub fn print_redirect<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
let data = UserData::from_read(r)?;
let mut list = Vec::with_capacity(data.redirect.len());
list.extend(
data.redirect
.iter()
.map(|(k, v)| (v.as_bytes(), k.as_bytes())),
);
list.sort();
writeln!(stdout, "\t{}", "Redirect List".yellow().bold().underline())?;
for (to, from) in list {
stdout.write_all(to)?;
stdout.write_all(b" ")?;
stdout.write_all(from)?;
stdout.write_all(b"\n")?;
}
stdout.flush()
}
pub fn print_sources<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
let data = UserData::from_read(r)?;
let mut list = Vec::with_capacity(data.sources.len());
list.extend(data.sources.keys().map(|k| k.as_bytes()));
list.sort();
writeln!(stdout, "\t{}", "Source List".yellow().bold().underline())?;
for i in list {
stdout.write_all(i)?;
stdout.write_all(b"\n")?;
}
stdout.flush()
}
pub(crate) fn is_valid_host<T: AsRef<str>>(value: T) -> bool {
let value = value.as_ref();
if value.is_empty()
|| value.starts_with(' ')
|| value.ends_with(' ')
|| value.starts_with('.')
|| value.ends_with('.')
|| !value.contains('.')
{
return false;
};
for c in value.chars() {
if c.is_ascii_uppercase()
|| c.is_ascii_lowercase()
|| c.is_ascii_digit()
|| (c == '.')
|| (c == '-')
|| (c == '_')
{
continue;
} else {
return false;
}
}
true
}
pub(crate) fn get_host_from_url<'a, T: AsRef<str> + ?Sized>(webs: &'a T) -> Option<&'a str> {
let url = webs.as_ref().trim_start();
let f = |v: &'a str| {
if let Some((host, _)) = v.split_once(['/', '?', '#', ':']) {
if is_valid_host(host) {
Some(host)
} else {
None
}
} else if is_valid_host(v) {
Some(v)
} else {
None
}
};
if url.starts_with("http") {
url.split_once("://").and_then(|(left, right)| {
if !matches!(left, "http" | "https") {
None
} else {
f(right)
}
})
} else {
f(url)
}
}
pub(crate) fn is_valid_url<T: AsRef<str>>(value: T) -> bool {
value
.as_ref()
.split_once(r"://")
.and_then(|(l, r)| {
if !matches!(l, "http" | "https") {
None
} else {
r.split_once('.')
}
})
.and_then(|(l, r)| {
if r.len() < 2
|| l.is_empty()
|| l.chars().any(|c| {
!matches!(&c,
'a'..='z' | 'A'..='Z' |
'0'..='9' | '_' | '-'
)
})
{
return None;
}
if let Some((i, c)) = r.chars().enumerate().find(|(_, c)| {
matches!(c, '/' | '?' | ':' | '#')
|| !matches!(c,
'a'..='z' | 'A'..='Z' |
'0'..='9' | '.' | '_' | '-'
)
}) {
match (i, c) {
(_, 'a'..='z' | 'A'..='Z' | '0'..='9' | '.' | '_' | '-' | '/' | '?' | '#') => {
Some(())
}
(index, ':') => {
let mut port: u16 = 0;
let zero = '0' as u16;
for i in r.chars().skip(index + 1) {
match i {
'0'..='9' => {
let v = i as u16 - zero;
if let Some(v) = port.checked_mul(10) {
port = v;
} else {
return None;
}
if let Some(u) = port.checked_add(v) {
port = u;
} else {
return None;
}
}
'/' | '?' | '#' => break,
_ => return None,
}
}
Some(())
}
_ => None,
}
} else {
Some(())
}
})
.is_some()
}
pub(crate) fn get_host_from_url_or_host(value: &str) -> Option<&str> {
if is_comment(value) {
return None;
};
let v: Vec<_> = value.split_whitespace().collect();
if v.is_empty() {
return None;
};
let u = if v.len() == 1 { v[0] } else { v[1] };
if is_valid_host(u) {
Some(u)
} else if is_valid_url(u) {
get_host_from_url(u)
} else {
None
}
}
pub(crate) fn is_comment<T: AsRef<str>>(value: T) -> bool {
let v = value.as_ref().trim_start();
if v.is_empty() || v.starts_with('#') {
return true;
};
false
}
pub(crate) fn sha256<T: AsRef<str>>(value: T) -> [u8; 32] {
let mut hasher = sha2::Sha256::new();
hasher.update(value.as_ref().as_bytes());
hasher.finalize().into()
}
#[cfg(test)]
mod tests {}