1use std::io;
2
3use colored::Colorize;
4use sha2::Digest;
5
6use crate::db::UserData;
7
8pub fn print_allow<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
9 let data = UserData::from_read(r)?;
10 let mut list = Vec::with_capacity(data.allow.len());
11 list.extend(data.allow.iter().map(|v| v.as_bytes()));
12 list.sort();
13 writeln!(stdout, "\t{}", "Allow List".yellow().bold().underline())?;
14 for i in list {
15 stdout.write_all(i)?;
16 stdout.write_all(b"\n")?;
17 }
18 stdout.flush()
19}
20pub fn print_block<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
21 let data = UserData::from_read(r)?;
22 let mut list = Vec::with_capacity(data.block.len());
23 list.extend(data.block.iter().map(|v| v.as_bytes()));
24 list.sort();
25 writeln!(stdout, "\t{}", "Block List".yellow().bold().underline())?;
26 for i in list {
27 stdout.write_all(i)?;
28 stdout.write_all(b"\n")?;
29 }
30 stdout.flush()
31}
32pub fn print_redirect<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
33 let data = UserData::from_read(r)?;
34 let mut list = Vec::with_capacity(data.redirect.len());
35 list.extend(
36 data.redirect
37 .iter()
38 .map(|(k, v)| (v.as_bytes(), k.as_bytes())),
39 );
40 list.sort();
41 writeln!(stdout, "\t{}", "Redirect List".yellow().bold().underline())?;
42 for (to, from) in list {
43 stdout.write_all(to)?;
44 stdout.write_all(b" ")?;
45 stdout.write_all(from)?;
46 stdout.write_all(b"\n")?;
47 }
48 stdout.flush()
49}
50pub fn print_sources<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
51 let data = UserData::from_read(r)?;
52 let mut list = Vec::with_capacity(data.sources.len());
53 list.extend(data.sources.keys().map(|k| k.as_bytes()));
54 list.sort();
55 writeln!(stdout, "\t{}", "Source List".yellow().bold().underline())?;
56 for i in list {
57 stdout.write_all(i)?;
58 stdout.write_all(b"\n")?;
59 }
60 stdout.flush()
61}
62
63pub(crate) fn is_valid_host<T: AsRef<str>>(value: T) -> bool {
64 let value = value.as_ref();
65 if value.is_empty()
66 || value.starts_with(' ')
67 || value.ends_with(' ')
68 || value.starts_with('.')
69 || value.ends_with('.')
70 || !value.contains('.')
71 {
72 return false;
73 };
74 for c in value.chars() {
75 if c.is_ascii_uppercase()
76 || c.is_ascii_lowercase()
77 || c.is_ascii_digit()
78 || (c == '.')
79 || (c == '-')
80 || (c == '_')
81 {
82 continue;
83 } else {
84 return false;
85 }
86 }
87 true
88}
89pub(crate) fn get_host_from_url<'a, T: AsRef<str> + ?Sized>(webs: &'a T) -> Option<&'a str> {
90 let url = webs.as_ref().trim_start();
91 let f = |v: &'a str| {
92 if let Some((host, _)) = v.split_once(['/', '?', '#', ':']) {
93 if is_valid_host(host) {
94 Some(host)
95 } else {
96 None
97 }
98 } else if is_valid_host(v) {
99 Some(v)
100 } else {
101 None
102 }
103 };
104 if url.starts_with("http") {
105 url.split_once("://").and_then(|(left, right)| {
106 if !matches!(left, "http" | "https") {
107 None
108 } else {
109 f(right)
110 }
111 })
112 } else {
113 f(url)
114 }
115}
116
117pub(crate) fn is_valid_url<T: AsRef<str>>(value: T) -> bool {
118 value
119 .as_ref()
120 .split_once(r"://")
121 .and_then(|(l, r)| {
122 if !matches!(l, "http" | "https") {
123 None
124 } else {
125 r.split_once('.')
126 }
127 })
128 .and_then(|(l, r)| {
129 if r.len() < 2
130 || l.is_empty()
131 || l.chars().any(|c| {
132 !matches!(&c,
133 'a'..='z' | 'A'..='Z' |
134 '0'..='9' | '_' | '-'
135 )
136 })
137 {
138 return None;
139 }
140 if let Some((i, c)) = r.chars().enumerate().find(|(_, c)| {
141 matches!(c, '/' | '?' | ':' | '#')
142 || !matches!(c,
143 'a'..='z' | 'A'..='Z' |
144 '0'..='9' | '.' | '_' | '-'
145 )
146 }) {
147 match (i, c) {
148 (_, 'a'..='z' | 'A'..='Z' | '0'..='9' | '.' | '_' | '-' | '/' | '?' | '#') => {
149 Some(())
150 }
151 (index, ':') => {
152 let mut port: u16 = 0;
153 let zero = '0' as u16;
154 for i in r.chars().skip(index + 1) {
155 match i {
156 '0'..='9' => {
157 let v = i as u16 - zero;
158 if let Some(v) = port.checked_mul(10) {
159 port = v;
160 } else {
161 return None;
162 }
163 if let Some(u) = port.checked_add(v) {
164 port = u;
165 } else {
166 return None;
167 }
168 }
169 '/' | '?' | '#' => break,
170 _ => return None,
171 }
172 }
173 Some(())
174 }
175 _ => None,
176 }
177 } else {
178 Some(())
179 }
180 })
181 .is_some()
182}
183
184pub(crate) fn get_host_from_url_or_host(value: &str) -> Option<&str> {
185 if is_comment(value) {
186 return None;
187 };
188 let v: Vec<_> = value.split_whitespace().collect();
189 if v.is_empty() {
190 return None;
191 };
192 let u = if v.len() == 1 { v[0] } else { v[1] };
193 if is_valid_host(u) {
194 Some(u)
195 } else if is_valid_url(u) {
196 get_host_from_url(u)
197 } else {
198 None
199 }
200}
201
202pub(crate) fn is_comment<T: AsRef<str>>(value: T) -> bool {
203 let v = value.as_ref().trim_start();
204 if v.is_empty() || v.starts_with('#') {
205 return true;
206 };
207 false
208}
209
210pub(crate) fn sha256<T: AsRef<str>>(value: T) -> [u8; 32] {
211 let mut hasher = sha2::Sha256::new();
212 hasher.update(value.as_ref().as_bytes());
213 hasher.finalize().into()
214}
215
216#[cfg(test)]
217mod tests {}