2023-05-28 18:27:17 +02:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
env,
|
|
|
|
error::Error,
|
|
|
|
fs,
|
|
|
|
io::{stdin, Read},
|
|
|
|
net::SocketAddr,
|
|
|
|
path::PathBuf,
|
|
|
|
};
|
2023-05-26 00:50:55 +02:00
|
|
|
|
2023-05-27 15:01:39 +02:00
|
|
|
use log::{debug, info};
|
2023-05-28 18:27:17 +02:00
|
|
|
use tokio::{fs::File, io::BufWriter, sync::mpsc::Sender};
|
2023-05-26 00:50:55 +02:00
|
|
|
|
|
|
|
use crate::crypto;
|
2023-05-22 03:54:49 +02:00
|
|
|
|
2023-05-25 01:20:56 +02:00
|
|
|
const PUBLIC_IPV4: &str = "https://ipinfo.io/ip";
|
|
|
|
const PUBLIC_IPV6: &str = "https://ipv6.icanhazip.com";
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq)]
|
|
|
|
pub enum Ip {
|
|
|
|
V4,
|
|
|
|
V6,
|
|
|
|
Local,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ip {
|
2023-05-28 18:27:17 +02:00
|
|
|
pub fn fetch(self, port: u16) -> Result<SocketAddr, Box<dyn Error>> {
|
2023-05-25 01:20:56 +02:00
|
|
|
let addr = match self {
|
|
|
|
Ip::V4 => PUBLIC_IPV4,
|
|
|
|
Ip::V6 => PUBLIC_IPV6,
|
2023-05-26 00:50:55 +02:00
|
|
|
Ip::Local => {
|
2023-05-25 01:20:56 +02:00
|
|
|
let addr_str = format!("127.0.0.1:{}", port);
|
2023-05-26 00:50:55 +02:00
|
|
|
return Ok(addr_str.parse::<SocketAddr>()?);
|
2023-05-25 01:20:56 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-05-28 18:27:17 +02:00
|
|
|
info!("Fetching IP information from {}", addr);
|
|
|
|
|
2023-05-25 01:20:56 +02:00
|
|
|
let res = format!("{}:{}", ureq::get(addr).call()?.into_string()?.trim(), port);
|
|
|
|
let addr = res.parse::<SocketAddr>()?;
|
|
|
|
|
2023-05-27 15:01:39 +02:00
|
|
|
debug!("IP: {}", res);
|
|
|
|
|
2023-05-25 01:20:56 +02:00
|
|
|
Ok(addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 00:50:55 +02:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct FileInfo {
|
|
|
|
pub name: String,
|
|
|
|
pub size: u64,
|
|
|
|
pub hash: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FileInfo {
|
|
|
|
pub fn new(name: String, size: u64, hash: String) -> Self {
|
|
|
|
Self { name, size, hash }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-27 21:09:48 +02:00
|
|
|
pub fn filepaths(
|
2023-05-28 18:27:17 +02:00
|
|
|
source: Option<PathBuf>,
|
2023-05-22 03:54:49 +02:00
|
|
|
files: Option<Vec<PathBuf>>,
|
2023-05-28 18:27:17 +02:00
|
|
|
) -> Result<Vec<PathBuf>, Box<dyn Error>> {
|
2023-05-27 15:01:39 +02:00
|
|
|
info!("Collecting filepaths");
|
|
|
|
|
2023-05-28 18:27:17 +02:00
|
|
|
let mut paths = Vec::new();
|
2023-05-22 03:54:49 +02:00
|
|
|
|
2023-05-28 18:27:17 +02:00
|
|
|
if let Some(source) = source {
|
|
|
|
let home = env::var("HOME")?;
|
|
|
|
let content = fs::read_to_string(source)?;
|
|
|
|
paths = content
|
|
|
|
.lines()
|
|
|
|
.into_iter()
|
|
|
|
.map(|p| PathBuf::from(p.replace('~', &home)))
|
|
|
|
.collect();
|
2023-05-27 15:01:39 +02:00
|
|
|
} else if let Some(files) = files {
|
2023-05-28 18:27:17 +02:00
|
|
|
paths = files;
|
2023-05-22 03:54:49 +02:00
|
|
|
}
|
|
|
|
|
2023-05-28 18:27:17 +02:00
|
|
|
debug!("Filepaths collection finished (total: {})", paths.len());
|
2023-05-27 15:01:39 +02:00
|
|
|
|
2023-05-28 18:27:17 +02:00
|
|
|
Ok(paths)
|
2023-05-22 03:54:49 +02:00
|
|
|
}
|
2023-05-26 00:50:55 +02:00
|
|
|
|
|
|
|
pub async fn metadata(
|
|
|
|
files: &Vec<PathBuf>,
|
2023-05-28 18:27:17 +02:00
|
|
|
) -> Result<(Vec<FileInfo>, HashMap<String, PathBuf>), Box<dyn Error>> {
|
2023-05-27 15:01:39 +02:00
|
|
|
info!("Collecting metadata");
|
|
|
|
|
2023-05-26 00:50:55 +02:00
|
|
|
let mut metadata = Vec::new();
|
|
|
|
let mut index = HashMap::new();
|
|
|
|
|
|
|
|
for path in files {
|
2023-05-28 18:27:17 +02:00
|
|
|
debug!("Collecting '{}' metadata", path.to_str().unwrap());
|
|
|
|
|
2023-05-26 00:50:55 +02:00
|
|
|
let split = path.to_str().unwrap().split('/').collect::<Vec<&str>>();
|
|
|
|
let name = split[split.len() - 1].to_string();
|
|
|
|
let handle = File::open(path).await?;
|
|
|
|
let size = handle.metadata().await?.len();
|
|
|
|
let hash = crypto::try_hash(path)?;
|
|
|
|
|
|
|
|
if size > 0 {
|
2023-05-27 15:01:39 +02:00
|
|
|
let info = FileInfo::new(name, size, hash.clone());
|
|
|
|
metadata.push(info);
|
2023-05-26 00:50:55 +02:00
|
|
|
index.insert(hash, path.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-27 15:01:39 +02:00
|
|
|
debug!(
|
|
|
|
"Metadata collection successfully done (total: {})",
|
|
|
|
metadata.len()
|
|
|
|
);
|
|
|
|
|
2023-05-26 00:50:55 +02:00
|
|
|
Ok((metadata, index))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn new_file(
|
|
|
|
mut path: PathBuf,
|
|
|
|
name: &str,
|
|
|
|
) -> Result<(BufWriter<File>, PathBuf), Box<dyn Error + Send + Sync>> {
|
2023-05-27 15:01:39 +02:00
|
|
|
debug!("New file handle for '{}'", name);
|
|
|
|
|
2023-05-26 00:50:55 +02:00
|
|
|
path.push(name);
|
|
|
|
let handle = File::create(&path).await?;
|
|
|
|
|
|
|
|
Ok((BufWriter::new(handle), path))
|
|
|
|
}
|
2023-05-28 18:27:17 +02:00
|
|
|
|
|
|
|
pub async fn handle_exit(tx: Sender<()>) -> Result<(), Box<dyn Error>> {
|
|
|
|
let mut stdin = stdin().lock().bytes();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let k = match stdin.next() {
|
|
|
|
None => continue,
|
|
|
|
Some(k) => k?,
|
|
|
|
};
|
|
|
|
|
|
|
|
if k == b'q' {
|
|
|
|
tx.send(()).await?;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|