use crate::*;
use reqwest::blocking::get;
use reqwest::blocking::ClientBuilder;
use reqwest::header;
use serde_json::from_str;
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::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> {
if consts::ARCH == Arch::Unsupported {
return Err(String::from("Unsupported Architexture"));
}
let dir = Path::new(&if consts::OS == OS::Linux {
if var("XDG_CONFIG_HOME").is_err() {
format!("{}/.config/playit/", var("HOME").unwrap())
} else {
format!("{}/playit/", var("XDG_CONFIG_HOME").unwrap())
}
} else if consts::OS == OS::Windows {
format!(r"{}\playit\", var("AppData").unwrap())
} else {
format!(
"{}/Library/Application Support/playit/",
var("HOME").unwrap()
)
})
.to_owned();
let config_file = {
let path = dir.join("config.json");
remove_file(&path).ok();
path
};
let connections = Vec::new();
let download_urls = Binaries {
win: Url::parse(&format!(
"https://playit.gg/downloads/playit-win_64-{}.exe",
consts::VERSION
))
.expect("Failed To Parse Download URL"),
lin: Url::parse(&format!(
"https://playit.gg/downloads/playit-linux_64-{}",
consts::VERSION
))
.expect("Failed To Parse Download URL"),
mac: Url::parse(&format!(
"https://playit.gg/downloads/playit-darwin_64-{}.zip",
consts::VERSION
))
.expect("Failed To Parse Download URL"),
arm64: Url::parse(&format!(
"https://playit.gg/downloads/playit-aarch64-{}",
consts::VERSION
))
.expect("Failed To Parse Download URL"),
arm: Url::parse(&format!(
"https://playit.gg/downloads/playit-armv7-{}",
consts::VERSION
))
.expect("Failed To Parse Download URL"),
};
let binary = download(&dir, &download_urls).expect("Failed To Download PlayIt");
let playit = {
let mut env = HashMap::new();
env.insert(String::from("NO_BROWSER"), String::from("true"));
if let Some(opts) = playit_opts {
env.insert(
String::from("PREFERRED_TUNNEL"),
opts.PREFERRED_TUNNEL.unwrap_or_else(|| String::from("")),
);
env.insert(
String::from("PREFERRED_THRESHOLD"),
opts.PREFERRED_THRESHOLD.unwrap_or(50).to_string(),
);
env.insert(
String::from("NO_SPECIAL_LAN"),
opts.NO_SPECIAL_LAN.unwrap_or(false).to_string(),
);
}
exec(String::from(binary.to_str().unwrap()), None, Some(env)).unwrap()
};
let (agent_key, server) = loop {
let config =
from_str::<Config>(&read_to_string(&config_file).unwrap_or_else(|_| 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());
}
};
Ok(PlayIt {
req_client: {
let mut headers = header::HeaderMap::new();
let mut auth = 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()
},
os: consts::OS,
config_file,
arch: consts::ARCH,
dir,
destroyed: false,
tunnels: Vec::new(),
agent_key,
started: false,
playit,
server,
used_packets: 0,
free_packets: 0,
connections,
version: String::from(consts::VERSION),
download_urls,
binary,
binary_type: consts::BINARY_TYPE,
output: Vec::new(),
stdout: Vec::new(),
stderr: Vec::new(),
errors: Vec::new(),
warnings: Vec::new(),
api_path: Url::parse("https://api.playit.gg/").unwrap(),
})
}
pub fn exec(
cmd: String,
args: Option<Vec<String>>,
envs: Option<HashMap<String, String>>,
) -> Result<Child, String> {
let mut command = Command::new(&cmd);
command
.args(args.unwrap_or_else(Vec::new))
.envs(envs.unwrap_or_else(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(
command
.spawn()
.unwrap_or_else(|_| panic!("Failed To Start Process: {}", &cmd)),
);
Ok(command)
}
pub fn trim_newlines(string: &str) -> String {
return string.trim_end().replace('\n', " ").replace('\r', "");
}
fn download(dir: &Path, download_urls: &Binaries) -> Result<PathBuf, String> {
let file = dir.join(&format!(
"playit-{:#?}-{}.{}",
if consts::BINARY_TYPE == BinaryType::Unsupported {
return Err(String::from("Unsupported Architexture"));
} else {
consts::BINARY_TYPE
},
consts::VERSION,
if consts::OS == OS::Windows {
"exe"
} else if consts::OS == OS::Linux {
"bin"
} else {
"zip"
}
));
if file.exists() {
return Ok(file);
}
if !dir.exists() {
create_dir_all(dir).unwrap();
}
let mut _file = OpenOptions::new()
.create(true)
.read(true)
.write(true)
.truncate(true)
.open(&file)
.unwrap();
_file
.write_all(
&get(download_urls[consts::BINARY_TYPE].clone())
.unwrap()
.bytes()
.unwrap(),
)
.expect("Failed To Download PlayIt");
#[cfg(target_os = "macos")]
{
use std::fs::copy;
use std::fs::remove_dir_all;
use std::fs::File;
use zip::ZipArchive;
let mut zip = ZipArchive::new(_file).unwrap();
zip.extract(dir).unwrap();
let file = dir.join(format!(
"playit-{:#?}-{}.bin",
BinaryType::MacOS,
consts::VERSION,
));
copy(
dir.join(format!(
"playit-darwin_64-{}/playit-darwin_64-{}",
consts::VERSION,
consts::VERSION
)),
&file,
)
.unwrap();
remove_dir_all(dir.join(format!("playit-darwin_64-{}", consts::VERSION))).unwrap();
_file = File::open(file).unwrap();
}
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd"
))]
{
use std::os::unix::fs::PermissionsExt;
_file.metadata().permissions().set_mode(0o777);
}
Ok(file)
}