use crate::common::{FPath, FileType};
use crate::readers::blockreader::SUBPATH_SEP;
use crate::readers::helpers::{
filename_count_extensions, fpath_to_path, path_clone, path_to_fpath, remove_extension,
};
use std::borrow::Cow;
use std::ffi::OsStr;
use std::fs::File;
use std::path::Path;
extern crate mime_guess;
#[doc(hidden)]
pub use mime_guess::MimeGuess;
extern crate si_trace_print;
#[allow(unused_imports)]
use si_trace_print::{defn, defo, defx, defñ, den, deo, dex, deñ};
extern crate tar;
#[derive(Debug, Eq, PartialEq)]
pub enum ProcessPathResult {
FileValid(FPath, MimeGuess, FileType),
FileErrNoPermissions(FPath, MimeGuess),
FileErrNotSupported(FPath, MimeGuess),
FileErrNotAFile(FPath),
FileErrNotExist(FPath),
}
pub type ProcessPathResults = Vec<ProcessPathResult>;
const PARSEABLE_FILENAMES_FILE: [&str; 5] = [
"messages", "syslog", "faillog", "lastlog", "kernlog",
];
const UNPARSEABLE_FILENAMES_FILE: [&str; 3] = [
"wtmp", "btmp", "utmp",
];
pub fn mimeguess_to_filetype_str(mimeguess_str: &str) -> FileType {
defñ!("({:?})", mimeguess_str);
let lower: String = mimeguess_str.to_lowercase();
const PLAIN: &str = "plain";
const TEXT: &str = "text";
const TEXT_PLAIN: &str = "text/plain";
const TEXT_PLAIN_UTF8: &str = "text/plain; charset=utf-8";
const TEXT_STAR: &str = "text/*";
const UTF8_: &str = "utf-8";
const APP_GZIP: &str = "application/gzip";
const APP_XGZIP: &str = "application/x-gzip";
const APP_X_XZ: &str = "application/x-xz";
const APP_TAR: &str = "application/x-tar";
const APP_GTAR: &str = "application/x-gtar";
const APP_TARGZ: &str = "application/x-compressed";
match lower.as_str() {
PLAIN | TEXT | TEXT_PLAIN | TEXT_PLAIN_UTF8 | TEXT_STAR | UTF8_ => FileType::File,
APP_GZIP | APP_XGZIP => FileType::Gz,
APP_X_XZ => FileType::Xz,
APP_TAR | APP_GTAR => FileType::Tar,
APP_TARGZ => FileType::TarGz,
_ => FileType::Unknown,
}
}
pub fn mimeguess_to_filetype(mimeguess: &MimeGuess) -> FileType {
defn!("mimeguess_to_filetype({:?})", mimeguess);
let mut filetype_un: FileType = FileType::Unknown;
for mimeguess_ in mimeguess.iter() {
deo!("mimeguess_to_filetype: check {:?}", mimeguess_);
match mimeguess_to_filetype_str(mimeguess_.as_ref()) {
FileType::Unset => {}
FileType::Unparseable => {
filetype_un = FileType::Unparseable;
}
val => {
defx!("mimeguess_to_filetype: return {:?}", val);
return val;
}
}
}
defx!("mimeguess_to_filetype: return {:?}", filetype_un);
filetype_un
}
pub(crate) fn path_to_filetype(path: &Path) -> FileType {
defn!("({:?})", path);
let file_name: &OsStr = path
.file_name()
.unwrap_or_default();
deo!("file_name {:?}", file_name);
let file_name_string: String = file_name
.to_str()
.unwrap_or_default()
.to_ascii_lowercase();
let file_name_s: &str = file_name_string.as_str();
deo!("file_name_s {:?}", file_name_s);
if PARSEABLE_FILENAMES_FILE.contains(&file_name_s) {
defx!("return File; PARSEABLE_FILENAMES_FILE.contains({:?})", file_name_s);
return FileType::File;
}
if UNPARSEABLE_FILENAMES_FILE.contains(&file_name_s) {
defx!("return File; PARSEABLE_FILENAMES_FILE.contains({:?})", file_name_s);
return FileType::Unparseable;
}
let file_prefix = path
.file_stem()
.unwrap_or_default();
let file_prefix_string: String = file_prefix
.to_str()
.unwrap_or_default()
.to_ascii_lowercase();
let file_prefix_s: &str = file_prefix_string.as_str();
deo!("file_prefix {:?}", file_prefix_s);
let file_suffix: &OsStr = path
.extension()
.unwrap_or_default();
let file_suffix_string: String = file_suffix
.to_str()
.unwrap_or_default()
.to_ascii_lowercase();
let file_suffix_s = file_suffix_string.as_str();
deo!("file_suffix {:?}", file_suffix_s);
if file_suffix_s == "journal" {
defx!("return Unparseable; .journal");
return FileType::Unparseable;
}
if file_suffix_s == "tgz" {
defx!("return TarGz; .tgz");
return FileType::TarGz;
}
if file_prefix_s.ends_with(".tgz") {
defx!("return TarGz; data.tgz");
return FileType::TarGz;
}
if file_name_s.ends_with(".gz.old") {
defx!("return Gz; .gz.old");
return FileType::Gz;
}
if file_suffix_s == "gzip" {
defx!("return Gz; .gzip");
return FileType::Gz;
}
if file_suffix_s == "gz" {
defx!("return Gz; .gz");
return FileType::Gz;
}
if file_name_s.ends_with(".xz.old") {
defx!("return Xz; .xz.old");
return FileType::Xz;
}
if file_suffix_s == "xzip" {
defx!("return Xz; .xzip");
return FileType::Xz;
}
if file_suffix_s == "xz" {
defx!("return Xz; .xz");
return FileType::Xz;
}
if file_name_s.ends_with(".tar.old") {
defx!("return Tar; .tar.old");
return FileType::Tar;
}
if file_suffix_s == "tar" {
defx!("return Tar; .tar");
return FileType::Tar;
}
if file_prefix_s.starts_with("log_") {
defx!("return File; log_");
return FileType::File;
}
if file_name_s.ends_with("_log") {
defx!("return File; _log");
return FileType::File;
}
if file_name_s.ends_with(".log.old") {
defx!("return File; .log.old");
return FileType::File;
}
if file_prefix_s == "syslog" {
defx!("return File; syslog.");
return FileType::File;
}
if file_name_s == "syslog" {
defx!("return File; syslog");
return FileType::File;
}
if file_name_s == "kernellog" {
defx!("return File; kernellog");
return FileType::File;
}
if file_name_s == "kernelog" {
defx!("return File; kernelog");
return FileType::File;
}
if file_prefix_s == "messages" {
defx!("return File; messages.");
return FileType::File;
}
if file_prefix_s == "dmesg" {
defx!("return File; dmesg.");
return FileType::File;
}
if file_name_s == "dmesg" {
defx!("return File; dmesg");
return FileType::File;
}
if file_name_s.starts_with("log.") {
defx!("return File; log..");
return FileType::File;
}
if file_prefix_s == "log" {
defx!("return File; log.");
return FileType::File;
}
if file_name_s == "log" {
defx!("return File; log");
return FileType::File;
}
if path.extension().is_none() {
defx!("return File; no path.extension()");
return FileType::File;
}
defx!("return Unknown");
FileType::Unknown
}
#[doc(hidden)]
#[cfg(test)]
pub fn fpath_to_filetype(path: &FPath) -> FileType {
path_to_filetype(fpath_to_path(path))
}
pub fn parseable_filetype(filetype: &FileType) -> bool {
match filetype {
&FileType::Unparseable | &FileType::Unknown | &FileType::Unset => false,
_ => true,
}
}
pub fn path_to_filetype_mimeguess(path: &Path) -> (FileType, MimeGuess) {
defn!("({:?})", path);
let mut mimeguess: MimeGuess = MimeGuess::from_path(path);
deo!("mimeguess {:?}", mimeguess);
if mimeguess.is_empty() {
let mut fpath: FPath;
let mut path_: &Path = path_clone(path);
let mut ext_rm = 0;
while mimeguess.is_empty() && filename_count_extensions(path_) != 0 && ext_rm < 3 {
match remove_extension(path_) {
None => {}
Some(fpath_rm1ext) => {
defo!("no mimeguess found, try again with removed file extension {:?}", fpath_rm1ext);
fpath = fpath_rm1ext;
path_ = fpath_to_path(&fpath);
mimeguess = MimeGuess::from_path(path_);
defo!("mimeguess {:?}", mimeguess);
}
}
ext_rm += 1;
}
}
let mut filetype: FileType = mimeguess_to_filetype(&mimeguess);
match filetype {
FileType::Unknown | FileType::Unset => {
defo!("filetype {:?} not parseable", filetype);
let mut fpath: FPath;
let mut path_: &Path = path_clone(path);
filetype = path_to_filetype(path_);
defo!("filetype {:?}", filetype);
let mut ext_rm = 0;
while !parseable_filetype(&filetype) && filename_count_extensions(path_) != 0 && ext_rm < 3 {
match remove_extension(path_) {
None => {}
Some(fpath_rm1ext) => {
defo!("try again with removed file extension {:?}", path_);
fpath = fpath_rm1ext;
path_ = fpath_to_path(&fpath);
filetype = path_to_filetype(path_);
defo!("filetype {:?}", filetype);
}
}
ext_rm += 1;
}
}
_ => {}
}
defx!("return ({:?}, {:?})", filetype, mimeguess);
(filetype, mimeguess)
}
#[doc(hidden)]
#[cfg(test)]
pub(crate) fn fpath_to_filetype_mimeguess(path: &FPath) -> (FileType, MimeGuess) {
let path_: &Path = fpath_to_path(path);
path_to_filetype_mimeguess(path_)
}
pub fn process_path_tar(path: &FPath) -> Vec<ProcessPathResult> {
defn!("({:?})", path);
let file: File = File::open(path).unwrap();
let mut archive: tar::Archive<File> = tar::Archive::<File>::new(file);
let entry_iter: tar::Entries<File> = match archive.entries() {
Ok(val) => val,
Err(_err) => {
defx!("Err {:?}", _err);
return vec![];
}
};
let mut results = Vec::<ProcessPathResult>::new();
for entry_res in entry_iter {
let entry: tar::Entry<File> = match entry_res {
Ok(val) => val,
Err(_err) => {
deo!("entry Err {:?}", _err);
continue;
}
};
let header: &tar::Header = entry.header();
let etype: tar::EntryType = header.entry_type();
deo!("entry.header().entry_type() {:?}", etype);
if !etype.is_file() {
continue;
}
let subpath: Cow<Path> = match entry.path() {
Ok(val) => val,
Err(_err) => {
deo!("entry.path() Err {:?}", _err);
continue;
}
};
let subfpath: FPath = subpath
.to_string_lossy()
.to_string();
let _filetype_subpath: FileType;
let mimeguess: MimeGuess;
(_filetype_subpath, mimeguess) = path_to_filetype_mimeguess(&subpath);
let mut fullpath: FPath =
String::with_capacity(path.len() + SUBPATH_SEP.len_utf8() + subfpath.len() + 1);
fullpath.push_str(path.as_str());
fullpath.push(SUBPATH_SEP);
fullpath.push_str(subfpath.as_str());
deo!("push FileValid({:?}, {:?}, {:?})", fullpath, mimeguess, FileType::Tar);
results.push(ProcessPathResult::FileValid(fullpath, mimeguess, FileType::Tar));
}
defx!("process_path_tar({:?})", path);
results
}
pub fn process_path(path: &FPath) -> Vec<ProcessPathResult> {
defn!("({:?})", path);
let std_path: &Path = Path::new(path);
if !std_path.exists() {
defx!("return FileErrNotExist({:?})", path);
return vec![ProcessPathResult::FileErrNotExist(path.clone())];
}
if std_path.is_file() {
let filetype: FileType;
let mimeguess: MimeGuess;
(filetype, mimeguess) = path_to_filetype_mimeguess(std_path);
if filetype.is_archived() && filetype.is_supported() {
defñ!("return process_path_tar({:?})", path);
return process_path_tar(path);
}
let paths: Vec<ProcessPathResult> =
vec![ProcessPathResult::FileValid(path.clone(), mimeguess, filetype)];
defx!("({:?}) {:?}", path, paths);
return paths;
}
let mut paths: Vec<ProcessPathResult> = Vec::<ProcessPathResult>::new();
deo!("WalkDir({:?})…", path);
for entry in walkdir::WalkDir::new(path.as_str())
.follow_links(true)
.contents_first(true)
.sort_by_file_name()
.same_file_system(false)
{
let path_entry = match entry {
Ok(val) => {
deo!("Ok({:?})", val);
val
}
Err(_err) => {
deo!("Err({:?})", _err);
continue;
}
};
deo!("analayzing {:?}", path_entry);
let std_path_entry: &Path = path_entry.path();
let fpath_entry: FPath = path_to_fpath(std_path_entry);
if !path_entry
.file_type()
.is_file()
{
if path_entry
.file_type()
.is_dir()
{
continue;
}
deo!("Path not a file {:?}", path_entry);
paths.push(ProcessPathResult::FileErrNotAFile(fpath_entry));
continue;
}
let filetype: FileType;
let mimeguess: MimeGuess;
(filetype, mimeguess) = path_to_filetype_mimeguess(std_path_entry);
if filetype.is_archived() && filetype.is_supported() {
defo!("process_path_tar({:?})", std_path_entry);
for result in process_path_tar(&fpath_entry).into_iter() {
paths.push(result);
}
continue;
}
match filetype {
FileType::TarGz | FileType::Unparseable => {
deo!("Path not supported {:?}", std_path_entry);
paths.push(ProcessPathResult::FileErrNotSupported(fpath_entry, mimeguess));
}
FileType::Unset => {
eprintln!("ERROR: filetype {:?} for {:?}", filetype, std_path_entry);
}
_ => {
deo!("paths.push(FileValid(({:?}, {:?}, {:?})))", fpath_entry, mimeguess, filetype);
paths.push(ProcessPathResult::FileValid(fpath_entry, mimeguess, filetype));
}
}
}
defx!("return {:?}", paths);
paths
}