use chrono::Utc;
use std::path::{Path, PathBuf};
pub type AccountError = citadel_io::NetworkError;
#[derive(Debug)]
pub struct CNACMetadata {
pub cid: u64,
pub username: String,
pub full_name: String,
pub is_personal: bool,
pub creation_date: String,
}
impl PartialEq for CNACMetadata {
fn eq(&self, other: &Self) -> bool {
self.cid == other.cid
&& self.username == other.username
&& self.full_name == other.full_name
&& self.is_personal == other.is_personal
}
}
pub fn get_present_formatted_timestamp() -> String {
Utc::now().to_rfc3339()
}
pub fn validate_virtual_path<R: AsRef<Path>>(virtual_path: R) -> Result<(), AccountError> {
let virtual_path = virtual_path.as_ref();
#[cfg(not(target_os = "windows"))]
const REQUIRED_BEGINNING: &str = "/";
#[cfg(target_os = "windows")]
const REQUIRED_BEGINNING: &str = "\\";
if !virtual_path.starts_with(REQUIRED_BEGINNING) {
return Err(citadel_io::error!(
citadel_io::ErrorCode::VirtualPathNotRemoteDir,
citadel_io::Dbg(virtual_path.to_path_buf())
));
}
let buf = format!("{}", virtual_path.display());
if buf.ends_with(REQUIRED_BEGINNING) {
return Err(citadel_io::error!(
citadel_io::ErrorCode::VirtualPathIsDirectory,
citadel_io::Dbg(virtual_path.to_path_buf())
));
}
if buf.contains("..") {
return Err(citadel_io::error!(
citadel_io::ErrorCode::VirtualPathTraversal,
citadel_io::Dbg(virtual_path.to_path_buf())
));
}
Ok(())
}
pub fn prepare_virtual_path<P: AsRef<Path>>(path: P) -> PathBuf {
let path = format!("{}", path.as_ref().display());
format_path(path).into()
}
#[cfg(not(target_os = "windows"))]
pub fn format_path(input: String) -> String {
input.replace('\\', "/")
}
#[cfg(target_os = "windows")]
pub fn format_path(input: String) -> String {
input.replace("/", "\\")
}
pub const VIRTUAL_FILE_METADATA_EXT: &str = ".vxe";
#[cfg(test)]
mod tests {
use crate::misc::{prepare_virtual_path, validate_virtual_path};
use rstest::rstest;
use std::path::PathBuf;
#[rstest]
#[case("/hello/world/tmp.txt")]
#[case("/hello/world/tmp")]
#[case("/tmp.txt")]
#[case("\\hello\\world\\tmp.txt")]
#[case("\\hello\\world\\tmp")]
#[case("\\tmp.txt")]
fn test_virtual_dir_formatting_okay(#[case] good_path: &str) {
let virtual_dir = PathBuf::from(good_path);
let formatted = prepare_virtual_path(virtual_dir);
validate_virtual_path(formatted).unwrap();
}
#[rstest]
#[case("/hello/")]
#[case("/")]
#[case("tmp.txt")]
#[case("\\hello\\")]
#[case("\\")]
#[case("/hello/world/../tmp.txt")]
fn test_virtual_dir_formatting_bad(#[case] bad_path: &str) {
let virtual_dir = PathBuf::from(bad_path);
let formatted = prepare_virtual_path(virtual_dir);
assert!(validate_virtual_path(formatted).is_err());
}
}