use crate::types::*;
use reqwest::header;
use reqwest::Client;
use reqwest::ClientBuilder;
use serde_json::from_str;
use std::collections::HashMap;
use std::env::consts;
use std::env::temp_dir;
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::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;
use std::thread::sleep;
use std::time::Duration;
use url::Url;
pub async fn get_defaults(playit_opts: Option<PlayItOpts>) -> Result<PlayIt, String> {
let os: String = String::from(if cfg!(unix) {
"lin"
} else if cfg!(target_os = "windows") {
"win"
} else if cfg!(target_os = "macos") {
"mac"
} else {
return Err(String::from("Not On A Supported Operating System"));
});
let arch: String = String::from({
if !["x86_64", "arm", "aarch64"].contains(&consts::ARCH) {
return Err(format!("Unsupported Architexture, {}", consts::ARCH));
} else if consts::ARCH == "x86_64" {
"x64"
} else if consts::ARCH == "aarch64" {
"arm64"
} else {
"arm"
}
});
let dir: PathBuf = {
let mut dir = temp_dir();
dir.push("playit");
create_dir_all(&dir).expect("Failed To Create The Temporary Directory");
dir
};
let config_file: PathBuf = {
let path = Path::new(&if os == String::from("lin") {
if var("XDG_CONFIG_HOME").is_err() {
format!("{}/.config/playit/config.json", var("HOME").unwrap())
} else {
format!("{}/playit/config.json", var("XDG_CONFIG_HOME").unwrap())
}
} else if os == "win" {
format!("{}/playit/config.json", var("AppData").unwrap())
} else {
format!(
"{}/Library/Application Support/playit/config.json",
var("HOME").unwrap()
)
})
.to_owned();
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("0.4.6");
let download_urls: HashMap<String, Url> = {
let mut download_urls: HashMap<String, Url> = HashMap::new();
download_urls.insert(
String::from("win"),
Url::parse(&*format!(
"https://playit.gg/downloads/playit-win_64-{}.exe",
version
))
.expect("Failed To Parse Download URL"),
);
download_urls.insert(
String::from("lin"),
Url::parse(&*format!(
"https://playit.gg/downloads/playit-linux_64-{}",
version
))
.expect("Failed To Parse Download URL"),
);
download_urls.insert(
String::from("mac"),
Url::parse(&*format!(
"https://playit.gg/downloads/playit-darwin_64-{}.zip",
version
))
.expect("Failed To Parse Download URL"),
);
download_urls.insert(
String::from("arm64"),
Url::parse(&*format!(
"https://playit.gg/downloads/playit-aarch64-{}",
version
))
.expect("Failed To Parse Download URL"),
);
download_urls.insert(
String::from("arm"),
Url::parse(&*format!(
"https://playit.gg/downloads/playit-armv7-{}",
version
))
.expect("Failed To Parse Download URL"),
);
download_urls
};
let binary_type: String = String::from(if os == "win" {
"win"
} else if os == "mac" {
"mac"
} else if os == "lin" && (arch == "arm" || arch == "arm64") {
&*arch
} else {
"lin"
});
let binary: PathBuf = download(
&String::from(dir.to_str().unwrap()),
&binary_type,
&version,
&os,
&download_urls,
)
.await;
let playit: Child = {
let mut env = HashMap::new();
env.insert(String::from("NO_BROWSER"), String::from("true"));
if !playit_opts.clone().is_none() {
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();
while !config_file.exists() {}
let (agent_key, server) = loop {
let config: Config = from_str::<Config>(
&*read_to_string(&config_file).expect("Failed To Read PlayIt Config File"),
)
.expect("Failed to Parse PlayIt Config File");
if !config.agent_key.is_none() && !config.preferred_tunnel.is_none() {
break (config.agent_key.unwrap(), config.preferred_tunnel.unwrap());
}
sleep(Duration::from_secs(1));
};
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 async fn download(
dir: &String,
binary_type: &String,
version: &String,
os: &String,
download_urls: &HashMap<String, Url>,
) -> PathBuf {
let file: PathBuf = Path::new(&format!(
"{}/playit-{}-{}.{}",
dir,
binary_type,
version,
String::from(if os == "win" { "exe" } else { "bin" })
))
.to_owned();
if file.exists() {
return file;
}
let mut _file: File = File::create(&file).expect("Failed To Download PlayIt");
_file
.write(
&reqwest::get(download_urls[&*binary_type].clone())
.await
.unwrap()
.bytes()
.await
.expect("Failed To Download PlayIt"),
)
.expect("Failed To Download PlayIt");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
_file.metadata().unwrap().permissions().set_mode(0x555);
}
return file;
}