#![feature(async_closure)]
#![feature(decl_macro)]
use lazy_static::lazy_static;
use structopt::StructOpt;
#[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(
author = "Anonymous",
about = "Serve files in the specified directory and subdirectories"
)]
struct Arguments {
#[structopt(long, short, default_value = "8787")]
pub port: u16,
#[structopt(long, short)]
pub verbose: bool,
#[structopt(long, short)]
pub localhost: bool,
#[structopt(default_value = ".")]
pub folder: String,
}
lazy_static! {
static ref ARGUMENTS: Arguments = Arguments::from_args();
}
#[allow(warnings)]
pub mod file_show_server{
use std::borrow::Cow;
use std::fs;
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use rocket::http::hyper::Request;
use rocket::response::content;
use rocket::{get, Response, routes, Config, catch, catchers, Responder, post, UriDisplayPath};
use rocket::http::ContentType;
use rocket_contrib::json::{Json as JSON, JsonValue};
use rocket::Data;
use tokio::io::AsyncReadExt;
use uuid::Uuid;
use std::io::{self, Write};
use rocket::http::Status;
use rocket::request::{FromRequest, Outcome};
use crate::ARGUMENTS;
use crate::file_show_routes::routes;
use log::info;
pub async fn start(){
let ip_addr: IpAddr = match local_ipaddress::get() {
Some(x)=>{
if ARGUMENTS.localhost{
IpAddr::from([127, 0, 0, 1])
}else{
x.parse().expect("Found local IP Address is invalid")
}},
None=>IpAddr::from([127, 0, 0, 1])
};
let socket_addr = SocketAddr::from((ip_addr, ARGUMENTS.port));
info!("Server listening on http://{}", socket_addr);
warp::serve(routes()).bind(socket_addr).await;
}
}
pub mod file_upload_server{
use log::info;
use rocket::{Data, post, routes, Rocket, get, response::content::Json};
use rocket_contrib::serve::StaticFiles;
use std::{fs, path::PathBuf, net::{IpAddr, SocketAddr}};
#[allow(warnings)]
#[post("/", data = "<data>")]
fn upload(data: Data) -> Result<Json<&'static str>, std::io::Error> {
let path = "./stream.data";
let pathbuf = PathBuf::from(path);
if pathbuf.exists() {
fs::remove_file(pathbuf.clone()).unwrap();
}
let mut file = fs::File::create(path)?;
data.stream_to_file(path)
.map(|n| format!("Wrote {} bytes to stream.data", n)).unwrap();
if let Ok(buf) = fs::read(path) {
if !buf.starts_with(&[0x2D,0x2D,0x2D,0x2D,0x2D,0x2D]){
if let Some(ex) = get_file_extension(&buf) {
fs::copy(path, format!("./stream.{}", ex)).unwrap();
} else {
fs::copy(path, format!("./stream")).unwrap();
}
}else{
use doe::*;
let mut buf = buf.clone();
let remove_last = buf.split_at(buf.len()-46).0.to_vec();
let remove_first_contet = remove_last.split_at(97).1;
let (head,other) = remove_first_contet.split_at(200);
let s = vec_element_to_string!(head).join(":");
let file_name = vec_element_clone!(split_to_vec!(String::from_utf8_lossy(head),"\""),0);
let s = split_to_vec!(s,":13:10:13:10:").last().unwrap().to_string();
let mut s_vec = split_to_vec!(s,":").into_iter().filter(|s|!s.is_empty()).map(|s|s.trim().to_string().parse::<u8>().unwrap()).collect::<Vec<u8>>();
s_vec.extend(other.iter());
std::fs::write(file_name, s_vec).unwrap();
}
}
if pathbuf.exists() {
fs::remove_file(pathbuf).unwrap();
}
Ok(Json("{\"status\":\"Ok\"}"))
}
#[allow(warnings)]
use rocket::response::{content, Content};
use rocket::http::ContentType;
#[get("/")]
fn index() -> Content<&'static str> {
let content_type = ContentType::HTML;
let body = include_str!("./html/index.html");
Content(content_type, body)
}
#[allow(warnings)]
fn get_file_extension(data: &[u8]) -> Option<&'static str> {
let ext = match data[..] {
[b'M', b'Z', ..] => Some("exe"),
[0x7f, b'E', b'L', b'F', ..] => Some("elf"),
[b'\x89', b'P', b'N', b'G', ..] => Some("png"),
[b'<', b'!', b'D', b'O', b'C', b'T', ..] => Some("html"),
[b'%', b'P', b'D', b'F', ..] => Some("pdf"),
[b'P', b'K', 3, 4, ..] => Some("zip"),
[0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c, ..] => Some("7z"),
[b'R', b'a', b'r', b'!', ..] => Some("rar"),
[b'\x1f', b'\x8b', 8, 0, ..] => Some("gz"),
[b'B', b'Z', b'h', ..] => Some("bz2"),
[b'I', b's', b'c', b'(', ..] => Some("cpio"),
[b'u', b's', b't', b'a', b'r', ..] => Some("tar"),
[b'I', b'T', b'S', b'F', ..] => Some("chm"),
[b'I', b'D', b'3', ..] => Some("mp3"),
[b'R', b'I', b'F', b'F', ..] => Some("avi"),
[b'\xff', b'\xd8', b'\xff', ..] => Some("jpg"),
[b'B', b'M', ..] => Some("bmp"),
[0, 1, ..] => Some("ico"),
[b'R', b'I', b'F', b'F', ..] => Some("wav"),
[b'O', b'g', b'g', b'S', ..] => Some("ogg"),
[b'f', b'L', b'a', b'C', ..] => Some("flac"),
[b'I', b'M', b'P', b'S', ..] => Some("impress"),
[b'M', b'T', b'h', b'd', ..] => Some("midi"),
[b'M', b'M', 0, 42, ..] => Some("tiff"),
[b'I', b'I', 0x2a, 0, 0x10, 0, 0, 0, b'C', b'R', ..] => Some("cr2"),
[b'M', b'M', 0, 0x2a, ..] => Some("nef"),
[b'W', b'E', b'B', b'P', ..] => Some("webp"),
[b'M', b'S', b'C', b'F', ..] => Some("cab"),
[b'P', b'K', 3, 4, ..] => Some("xlsx"),
[b'\xfd', b'7', b'z', b'X', b'Z', 0, ..] => Some("xz"),
[b'B', b'E', b'G', b'I', b'N', b':', b'V', b'C', ..] => Some("ics"),
[b'B', b'E', b'G', b'I', b'N', b':', b'V', b'C', b'A', b'R', b'D', ..] => Some("vcf"),
[b'S', b'Q', b'L', b'i', b't', b'e', b' ', b'f', b'o', b'r', b'm', b'a', b't', b' ', b'3', ..] => Some("sqlite"),
[b'G', b'I', b'F', b'8', b'7', b'a', ..] |
[b'G', b'I', b'F', b'8', b'9', b'a', ..] => Some("gif"),
_ => None,
};
ext
}
pub async fn start() {
let ip_addr: IpAddr = match local_ipaddress::get() {
Some(x)=>{
x.parse().expect("Found local IP Address is invalid")
},
None=>IpAddr::from([127, 0, 0, 1])
};
use rocket::config::{Config, Environment};
let socket_addr = SocketAddr::from((ip_addr, 8585));
info!("Server listening on http://{}", socket_addr);
let config = Config::build(Environment::Production)
.address(socket_addr.ip().to_string()) .port(socket_addr.port()) .finalize()
.unwrap();
Rocket::custom(config)
.mount("/", StaticFiles::from("static"))
.mount("/", routes![upload,index])
.launch();
}
}
#[allow(warnings)]
pub mod start_all_server{
use log::{info, LevelFilter};
use std::net::{IpAddr, SocketAddr};
use structopt::StructOpt;
use crate::ARGUMENTS;
fn initialize_logger() {
let level = if ARGUMENTS.verbose {
LevelFilter::Debug
} else {
LevelFilter::Info
};
env_logger::builder()
.format_module_path(false)
.filter(Some("serve"), level)
.init()
}
pub async fn start() {
use tokio::signal::ctrl_c;
initialize_logger();
let handle1 = tokio::spawn(async move {
crate::file_show_server::start().await;
});
let handle2 = tokio::spawn(async move {
crate::file_upload_server::start().await;
});
ctrl_c().await.expect("Unalbe to get Ctrl+C signal");
info!("Ctrl+C received. Shutting down");
handle1.abort();
handle2.abort();
std::process::exit(1);
handle1.await.unwrap_or(());
handle2.await.unwrap_or(());
}
}
pub mod file_show_routes{
use build_html::*;
use log::debug;
use std::fs::read_dir;
use std::path::{Path, PathBuf};
use warp::filters::BoxedFilter;
use warp::path::FullPath;
use warp::reject::not_found;
use warp::reply::{html, Reply};
use warp::Filter;
use crate::ARGUMENTS;
pub fn routes() -> BoxedFilter<(impl Reply,)> {
let logging = warp::log::custom(|info| {
debug!("Request: '{}',\tStatus: '{}'", info.path(), info.status())
});
let handle_files = warp::fs::dir(&ARGUMENTS.folder);
let handle_directories = warp::get()
.and(warp::path::full())
.and_then(path_to_html)
.map(html);
handle_files.or(handle_directories).with(logging).boxed()
}
async fn path_to_html(route: FullPath) -> Result<String, warp::reject::Rejection> {
let path = PathBuf::from(&ARGUMENTS.folder).join(&route.as_str()[1..]);
let content = HtmlPage::new()
.with_style(include_str!("styles.css"))
.with_container(
Container::new(ContainerType::Main)
.with_attributes([("class", "border-box")])
.with_preformatted_attr(route.as_str(), [("id", "header")])
.with_container(links_container(path.as_path(), &route).ok_or_else(not_found)?),
)
.to_html_string();
Ok(content)
}
fn links_container(path: &Path, route: &FullPath) -> Option<Container> {
let content_attrs = [("class", "content")];
let mut links = Container::new(ContainerType::Div).with_attributes([("id", "wrapper")]);
if route.as_str() != "/" {
let parent = path
.parent()
.and_then(|path| path.strip_prefix(&ARGUMENTS.folder).ok())
.and_then(Path::to_str)
.map(|s| format!("/{}", s))?;
links.add_link_attr(parent, "..", content_attrs);
}
let mut entries: Vec<(String, String, &'static str)> = read_dir(&path)
.ok()?
.filter_map(|res| res.ok().map(|x| x.path()))
.filter_map(format_path)
.collect();
entries.sort_by_cached_key(|(_, name, _)| name.to_string());
for (path, name, icon) in entries {
let link_text = format!("{}<p class=\"text\">{}</p>", icon, name);
links.add_link_attr(path, link_text, content_attrs);
}
Some(links)
}
fn format_path(path: PathBuf) -> Option<(String, String, &'static str)> {
let net_path = format!("/{}", path.strip_prefix(&ARGUMENTS.folder).ok()?.to_str()?);
let file_name = path.file_name()?.to_str()?.into();
let icon = if path.is_dir() {
include_str!("./folder_icon.svg")
} else {
include_str!("./file_icon.svg")
};
Some((net_path, file_name, icon))
}
}