use std::{fs, path::Path, net::{IpAddr,SocketAddr}};
use axum::http::{header::FORWARDED, HeaderMap};
use forwarded_header_value::{ForwardedHeaderValue, Identifier};
use crate::defs::{
Config as ServerConfig,
AUTHORIZATION,
REFERER,
BEARER,
USER_AGENT,
ACCEPT_LANGUAGE,
X_FORWARDED_FOR,
X_REAL_IP,
X_INTERNAL,
SESSION_ID,
FILE_SCHEME,
AppDBs,
UserId,
};
use crate::defs::UserNotifyData;
pub struct ReqHeaderMap {
pub header: HeaderMap,
pub req_path: Vec<String>,
}
impl ReqHeaderMap {
pub fn new(header: HeaderMap, request_path: &str) -> Self {
let req_path =
Self::req_end_path(request_path)
.split(",").map(|s| s.to_string())
.collect();
ReqHeaderMap {
header,
req_path,
}
}
pub fn req_end_path(req_path: &str) -> String {
let arr_req_path: Vec<String> = req_path.split("/").map(|s| s.to_string()).collect();
format!("{}",arr_req_path[arr_req_path.len()-1])
}
pub fn req_user(&self, app_dbs: &AppDBs) -> (String, UserNotifyData) {
let token = self.auth();
if !token.is_empty() {
return UserNotifyData::from_token(&token, &app_dbs.config.paseto);
} else {
let arr_req_path: Vec<String> = self.req_path.iter()
.filter(|it| it.contains(SESSION_ID))
.map(|s| s.to_string())
.collect();
if arr_req_path.len() > 0 && arr_req_path[0].contains(SESSION_ID) {
let req_sid = format!("{}",&arr_req_path[0].replace(SESSION_ID, ""));
if let Some(uid) = UserId::from(&req_sid) {
let id_path = format!("{}/{}", &app_dbs.config.users_store_uri.replace(FILE_SCHEME, ""), &uid);
if ! Path::new(&id_path).exists() {
let _ = fs::create_dir(&id_path);
}
let id = format!("{}",uid);
return (
id.to_owned(),
UserNotifyData{
key: String::from(SESSION_ID),
auth: id.to_owned(),
id,
}
);
}
}
}
(String::from(""), UserNotifyData::default())
}
#[allow(dead_code)]
pub fn req_path_clean(&self) -> Vec<String> {
let arr_value: Vec<String> =
self.req_path.iter()
.filter(|it| !it.contains(SESSION_ID))
.map(|s| s.to_string())
.collect();
if arr_value.len() == 1 && arr_value[0].is_empty() {
Vec::new()
} else {
arr_value
}
}
#[allow(dead_code)]
pub fn req_path_contains(&self,value: &str) -> Vec<String> {
let arr_value: Vec<String> =
self.req_path.iter()
.filter(|it| it.contains(value))
.map(|s| s.to_string())
.collect();
if arr_value.len() == 0 || arr_value[0].is_empty() {
Vec::new()
} else {
arr_value
}
}
pub fn auth(&self) -> String {
if let Some(auth) = self.header.get(AUTHORIZATION) {
format!("{}",auth.to_str().unwrap_or("").replace(&format!("{} ", BEARER),""))
} else {
String::from("")
}
}
#[allow(dead_code)]
pub fn referer(&self) -> String {
if let Some(referer) = self.header.get(REFERER) {
format!("{}",referer.to_str().unwrap_or(""))
} else {
String::from("")
}
}
#[allow(dead_code)]
pub fn internal(&self) -> String {
if let Some(internal) = self.header.get(X_INTERNAL) {
format!("{}",internal.to_str().unwrap_or(""))
} else {
String::from("")
}
}
pub fn is_browser(&self) -> bool {
if let Some(user_agent) = self.header.get(USER_AGENT) {
let agent = user_agent.to_str().unwrap_or("");
if agent.contains("Mozilla") {
true
} else if agent.contains("WebKit") {
true
} else if agent.contains("Chrome") {
true
} else {
false
}
} else {
false
}
}
#[allow(dead_code)]
pub fn is_curl(&self) -> bool {
if let Some(user_agent) = self.header.get(USER_AGENT) {
let agent = user_agent.to_str().unwrap_or("");
if agent.contains("curl") {
true
} else {
false
}
} else {
false
}
}
pub fn is_wget(&self) -> bool {
if let Some(user_agent) = self.header.get(USER_AGENT) {
let agent = user_agent.to_str().unwrap_or("").to_lowercase();
if agent.contains("wget") {
true
} else {
false
}
} else {
false
}
}
#[allow(dead_code)]
pub fn response_user_agent_html(&self) -> bool {
if let Some(user_agent) = self.header.get(USER_AGENT) {
let agent = user_agent.to_str().unwrap_or("").to_lowercase();
agent.contains("curl") || agent.contains("wget")
} else {
false
}
}
#[allow(dead_code)]
pub fn agent(&self) -> String {
if let Some(user_agent) = self.header.get(USER_AGENT) {
user_agent.to_str().unwrap_or("").to_owned()
} else {
String::from("")
}
}
#[allow(dead_code)]
pub fn lang(&self, config: &ServerConfig) -> String {
if let Some(langs) = self.header.get(ACCEPT_LANGUAGE) {
let langs_data = langs.to_str().unwrap_or("");
if langs_data.is_empty() {
format!("{}",config.dflt_lang)
} else {
let arr_langs: Vec<String> = langs_data.split(",").map(|s| s.to_string()).collect();
let arr_lang: Vec<String> = arr_langs[0].split("-").map(|s| s.to_string()).collect();
format!("{}",arr_lang[0])
}
} else {
format!("{}",config.dflt_lang)
}
}
#[allow(dead_code)]
fn maybe_x_forwarded_for(&self) -> Option<IpAddr> {
self.header
.get(X_FORWARDED_FOR)
.and_then(|hv| hv.to_str().ok())
.and_then(|s| s.split(',').find_map(|s| s.trim().parse::<IpAddr>().ok()))
}
#[allow(dead_code)]
fn maybe_x_real_ip(&self) -> Option<IpAddr> {
self.header
.get(X_REAL_IP)
.and_then(|hv| hv.to_str().ok())
.and_then(|s| s.parse::<IpAddr>().ok())
}
#[allow(dead_code)]
fn maybe_forwarded(&self) -> Option<IpAddr> {
self.header
.get_all(FORWARDED).iter().find_map(|hv| {
hv.to_str()
.ok()
.and_then(|s| ForwardedHeaderValue::from_forwarded(s).ok())
.and_then(|f| {
f.iter()
.filter_map(|fs| fs.forwarded_for.as_ref())
.find_map(|ff| match ff {
Identifier::SocketAddr(a) => Some(a.ip()),
Identifier::IpAddr(ip) => Some(*ip),
_ => None,
})
})
})
}
#[allow(dead_code)]
pub fn ip(&self, addr: &SocketAddr) -> String {
if let Some(ip) = self.maybe_x_real_ip()
.or_else(||self.maybe_x_forwarded_for())
.or_else(|| self.maybe_forwarded())
.or_else(|| None )
{
format!("{}",ip)
} else {
format!("{}",addr)
}
}
}