use crate::consts::*;
use crate::types::*;
use reqwest::blocking::Client;
use reqwest::blocking::ClientBuilder;
use reqwest::header;
use serde_json::from_str;
use std::any::type_name;
use std::collections::HashMap;
use std::env::var;
use std::fs::create_dir_all;
use std::fs::read_to_string;
use std::fs::remove_file;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;
use url::Url;
pub fn get_defaults(playit_opts: Option<PlayItOpts>) -> Result<PlayIt, String> {
let os: String = String::from(if OS == "" {
return Err(String::from("Unsupported Operating System"));
} else {
String::from(OS)
});
let arch: String = String::from({
if ARCH == "" {
return Err(format!("Unsupported Architexture"));
} else {
ARCH
}
});
let dir: PathBuf = Path::new(&if os == "lin" {
if var("XDG_CONFIG_HOME").is_err() {
format!("{}/.config/playit/", var("HOME").unwrap())
} else {
format!("{}/playit/", var("XDG_CONFIG_HOME").unwrap())
}
} else if os == "win" {
format!("{}/playit/", var("AppData").unwrap())
} else {
format!(
"{}/Library/Application Support/playit/",
var("HOME").unwrap()
)
})
.to_owned();
let config_file: PathBuf = {
let path = dir.join("config.json");
remove_file(&path).ok();
path
};
let destroyed: bool = false;
let tunnels: Vec<Tunnel> = Vec::new();
let started: bool = false;
let used_packets: u8 = 0;
let free_packets: u8 = 0;
let connections: Vec<Connection> = Vec::new();
let version: String = String::from(VERSION);
let download_urls: Binaries = Binaries {
win: Url::parse(&format!(
"https://playit.gg/downloads/playit-win_64-{}.exe",
version
))
.expect("Failed To Parse Download URL"),
lin: Url::parse(&format!(
"https://playit.gg/downloads/playit-linux_64-{}",
version
))
.expect("Failed To Parse Download URL"),
mac: Url::parse(&format!(
"https://playit.gg/downloads/playit-darwin_64-{}.zip",
version
))
.expect("Failed To Parse Download URL"),
arm64: Url::parse(&format!(
"https://playit.gg/downloads/playit-aarch64-{}",
version
))
.expect("Failed To Parse Download URL"),
arm: Url::parse(&format!(
"https://playit.gg/downloads/playit-armv7-{}",
version
))
.expect("Failed To Parse Download URL"),
};
let binary_type: String = String::from(if os == "lin" && (arch == "arm" || arch == "arm64") {
ARCH
} else {
OS
});
let binary: PathBuf = download(&dir, &binary_type, &download_urls);
let playit: Child = {
let mut env = HashMap::new();
env.insert(String::from("NO_BROWSER"), String::from("true"));
if playit_opts.as_ref().is_some() {
env.insert(
String::from("PREFERRED_TUNNEL"),
playit_opts
.clone()
.unwrap()
.PREFERRED_TUNNEL
.unwrap_or(String::from("")),
);
env.insert(
String::from("PREFERRED_THRESHOLD"),
playit_opts
.clone()
.unwrap()
.PREFERRED_THRESHOLD
.unwrap_or(50)
.to_string(),
);
env.insert(
String::from("NO_SPECIAL_LAN"),
playit_opts
.clone()
.unwrap()
.NO_SPECIAL_LAN
.unwrap_or(false)
.to_string(),
);
}
exec(String::from(binary.to_str().unwrap()), None, Some(env)).unwrap()
};
let output: Vec<String> = Vec::new();
let stdout: Vec<String> = Vec::new();
let stderr: Vec<String> = Vec::new();
let errors: Vec<String> = Vec::new();
let warnings: Vec<String> = Vec::new();
let (agent_key, server) = loop {
let config: Config = from_str::<Config>(
&read_to_string(&config_file).unwrap_or(String::from("{}")),
)
.unwrap_or(Config {
agent_key: None,
preferred_tunnel: None,
});
if config.agent_key.is_some() && config.preferred_tunnel.is_some() {
break (config.agent_key.unwrap(), config.preferred_tunnel.unwrap());
}
};
let req_client: Client = {
let mut headers: header::HeaderMap = header::HeaderMap::new();
let mut auth: header::HeaderValue =
header::HeaderValue::from_str(&format!("agent {}", agent_key)).unwrap();
auth.set_sensitive(true);
headers.insert(header::AUTHORIZATION, auth);
ClientBuilder::new()
.default_headers(headers)
.build()
.unwrap()
};
let api_path = Url::parse("https://api.playit.gg/").unwrap();
return Ok(PlayIt {
req_client,
os,
config_file,
arch,
dir,
destroyed,
tunnels,
agent_key,
started,
playit,
server,
used_packets,
free_packets,
connections,
version,
download_urls,
binary,
binary_type,
output,
stdout,
stderr,
errors,
warnings,
api_path,
});
}
pub fn exec(
cmd: String,
args: Option<Vec<String>>,
envs: Option<HashMap<String, String>>,
) -> Result<Child, String> {
let mut command: Command = Command::new(&cmd);
command
.args(args.unwrap_or(Vec::new()))
.envs(envs.unwrap_or(HashMap::new()))
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.stdout(Stdio::piped());
#[cfg(target_os = "windows")]
{
use std::os::windows::process::CommandExt;
command.creation_flags(0x08000000);
}
let command: Child = Child(
command
.spawn()
.expect(&format!("Failed To Start Process: {}", &cmd)),
);
return Ok(command);
}
pub fn trim_newlines(string: &String) -> String {
return string.trim_end().replace('\n', " ").replace('\r', "");
}
pub fn download(dir: &PathBuf, binary_type: &String, download_urls: &Binaries) -> PathBuf {
let file: PathBuf = dir
.join(&format!(
"playit-{}-{}.{}",
binary_type,
VERSION,
String::from(if OS == "win" { "exe" } else { "bin" })
))
.to_owned();
if file.exists() {
return file;
}
if !file.parent().unwrap().exists() {
create_dir_all(file.parent().unwrap()).ok();
}
let mut _file: OpenOptions = OpenOptions::new();
_file.create(true).write(true).truncate(true);
#[cfg(any(
target_os = "macos",
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd"
))]
{
use std::os::unix::fs::OpenOptionsExt;
_file.mode(0o555);
}
let mut _file: File = _file.open(&file).unwrap();
_file
.write(
&reqwest::blocking::get(download_urls[binary_type].clone())
.unwrap()
.bytes()
.expect("Failed To Download PlayIt"),
)
.expect("Failed To Download PlayIt");
return file;
}
pub fn type_of<T>(_: &T) -> &str {
return type_name::<T>();
}