#!/usr/bin/env runrs //! ```cargo //! [dependencies] //! clap = "4.1.8" //! reqwest = { version = "0.11.14", features = ["blocking", "json"] } //! rust_util = "0.6.41" //! serde = { version = "1.0.152", features = ["derive"] } //! serde_json = "1.0.93" //! ``` use std::collections::HashMap; use std::fs; use std::path::PathBuf; use base64::Engine; use clap::{arg, Parser}; use rust_util::{debugging, failure_and_exit, information, success}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { /// Access Token, or from environment: POST_RS_TOKEN #[arg(short, long)] token: Option, /// FileName #[arg(short, long)] file_name: Option, /// Content Type #[arg(short, long)] content_type: Option, /// Description #[arg(short, long)] description: Option, /// Available after create, default 7d, 10m(10 minutes), 1h(1 hour), 1d(1 day), 1M(1 month), 1y(1 year) #[arg(long)] available_after_create: Option, /// Available after decrypt, default 10m, 10m(10 minutes), 1h(1 hour), 1d(1 day), 1M(1 month), 1y(1 year) #[arg(long)] available_after_decrypt: Option, /// Password #[arg(short, long)] pass: Option, #[arg(required = true)] file: PathBuf, } fn main() { let args: Args = Args::parse(); let token = args.token.unwrap_or_else( || std::env::var("POST_RS_TOKEN").unwrap_or_else( |_| failure_and_exit!("Token is required!"))); let file = args.file; if !file.is_file() { failure_and_exit!("File: {:?} not exists or it not a file.", file) } let file_name = args.file_name.unwrap_or_else( || file.file_name().map( |f| f.to_str().map(ToString::to_string)) .flatten().unwrap_or_else(|| "unnamed".to_string())); let content_type = args.content_type.unwrap_or_else(|| get_content_type(&file_name)); let description = args.description.unwrap_or_else(|| "n/a".to_string()); let available_after_create = args.available_after_create.unwrap_or_else(|| "7d".to_string()); let available_after_decrypt = args.available_after_decrypt.unwrap_or_else(|| "10m".to_string()); let password = args.pass; let file_bytes = fs::read(&file).unwrap_or_else(|e| { failure_and_exit!("Read file: {:?} failed: {}", file, e) }); let file_base64 = base64::engine::general_purpose::STANDARD.encode(&file_bytes); information!("Sending file: {}", file_name); let mut params = HashMap::new(); params.insert("pretty", "true"); params.insert("token", &token); params.insert("dataBase64", &file_base64); params.insert("fileName", &file_name); params.insert("contentType", &content_type); params.insert("description", &description); params.insert("availableAfterCreate", &available_after_create); params.insert("availableAfterDecrypt", &available_after_decrypt); debugging!("Params: {:?}", serde_json::to_string_pretty(¶ms)); if let Some(pass) = &password { params.insert("pass", pass); } let client = reqwest::blocking::Client::new(); let post_result = client.post("https://hatter.ink/secfile/post.json") .form(¶ms) .send(); match post_result { Err(e) => failure_and_exit!("Post file: {} failed: {}",file_name, e), Ok(respone) => match respone.text() { Err(e) => failure_and_exit!("Post file: {} response failed: {}", file_name, e), Ok(text) => success!("Post file: {} succeed: {}", file_name, text), } } } fn get_content_type(file_name: &str) -> String { if file_name.ends_with(".txt") { "text/plain".into() } else { "application/octet-stream".into() } }