use std::io;
use std::path::Path;
use std::path::PathBuf;
use std::collections::HashMap;
use std::string::String;
use reqwest::blocking::Client;
use install_framework_core::command::Interpreter;
use install_framework_core::command::InstallCommand;
use install_framework_core::interface::InstallMethod;
use super::base;
use crate::cache::Cache;
use crate::error::Error;
use crate::interface::SimpleInterpreter;
use crate::platform;
#[cfg(unix)]
fn check_and_assign_bin_perm(path: &Path) -> io::Result<()>
{
use std::os::unix::fs::PermissionsExt;
let meta = std::fs::metadata(path)?;
if meta.permissions().mode() != 755
{
let perms = PermissionsExt::from_mode(0o755);
std::fs::set_permissions(path, perms)?;
}
return Ok(());
}
#[cfg(target_os="linux")]
fn auto_create_desktop(cache: &Path, desktop_folder: &Path, content_dir: &Path, path: &str) -> io::Result<()>
{
use std::fs::File;
use std::io::Write;
let name = platform::common::get_target_desktop_name(path);
let mut copy = name.clone();
copy.push_str(".icon");
let mut copy1 = name.clone();
copy1.push_str(".desktop");
let icon = content_dir.join(copy);
let bin = content_dir.join(path);
let outpath = desktop_folder.join(copy1);
check_and_assign_bin_perm(&bin)?;
if path.ends_with(".AppImage") && !icon.exists()
{
use std::process::Command;
use std::fs::metadata;
use std::fs::read_link;
let mut res = Command::new(&bin).arg("--appimage-extract").current_dir(cache).spawn()?;
if !res.wait()?.success()
{
return Err(io::Error::new(io::ErrorKind::Other, "AppImage extraction failure"));
}
let icon_link = cache.join("squashfs-root").join(".DirIcon");
let meta = metadata(&icon_link)?;
if meta.file_type().is_symlink()
{
let target = read_link(&icon_link)?;
std::fs::copy(target, &icon)?;
}
else
{
std::fs::copy(icon_link, &icon)?;
}
std::fs::remove_dir_all(cache.join("squashfs-root"))?;
}
let mut f = File::create(outpath)?;
writeln!(f, "[Desktop Entry]")?;
writeln!(f, "Name=ERPMod")?;
writeln!(f, "Exec={:?}", &bin)?;
writeln!(f, "Terminal=false")?;
writeln!(f, "Type=Application")?;
let fuckyourust = format!("{:?}", icon); let fuckyourust1 = &fuckyourust[1..fuckyourust.len() - 1]; writeln!(f, "Icon={}", fuckyourust1)?;
return Ok(());
}
#[cfg(windows)]
fn auto_create_shortcut<TError: Error>(desktop_folder: &Path, content_dir: &Path, path: &str) -> Result<(), TError::ErrorType>
{
use winapi::um::objbase::CoInitialize;
use winapi::um::combaseapi::CoUninitialize;
use winapi::um::combaseapi::CoCreateInstance;
use winapi::shared::ntdef::NULL;
use winapi::um::shobjidl_core::IShellLinkW;
use winapi::um::objidl::IPersistFile;
use winapi::um::combaseapi::CLSCTX_ALL;
use winapi::shared::winerror::SUCCEEDED;
use winapi::DEFINE_GUID;
use std::os::windows::ffi::OsStrExt;
use winapi::shared::minwindef::TRUE;
use std::ffi::OsString;
use winapi::um::unknwnbase::LPUNKNOWN;
DEFINE_GUID!(CLSID_SHELLLINK, 0x00021401, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
DEFINE_GUID!(IID_ISHELLLINKW, 0x000214F9, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
DEFINE_GUID!(IID_IPERSISTFILE, 0x0000010B, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
let name = platform::common::get_target_desktop_name(path);
let mut name1 = name.clone();
name1.push_str(".lnk");
let bin = content_dir.join(path);
unsafe
{
let mut linka: *mut IShellLinkW = std::ptr::null_mut();
CoInitialize(NULL);
if !SUCCEEDED(CoCreateInstance(&CLSID_SHELLLINK, NULL as LPUNKNOWN, CLSCTX_ALL, &IID_ISHELLLINKW, &mut linka as *mut _ as _))
{
CoUninitialize();
return Err(TError::generic(String::from("IShellLinkW allocation failure")));
}
let mut wname: Vec<u16> = OsString::from(name).encode_wide().collect();
let mut wlink: Vec<u16> = bin.as_os_str().encode_wide().collect();
wname.push(0);
wlink.push(0);
linka.as_ref().unwrap().SetDescription(wname.as_ptr());
linka.as_ref().unwrap().SetPath(wlink.as_ptr());
linka.as_ref().unwrap().SetIconLocation(wlink.as_ptr(), 0);
let mut persist: *mut IPersistFile = std::ptr::null_mut();
if !SUCCEEDED(linka.as_ref().unwrap().QueryInterface(&IID_IPERSISTFILE, &mut persist as *mut _ as _))
{
linka.as_ref().unwrap().Release();
CoUninitialize();
return Err(TError::generic(String::from("IPersistFile allocation failure")));
}
let dest = desktop_folder.join(name1);
let mut wdest: Vec<u16> = dest.as_os_str().encode_wide().collect();
wdest.push(0);
persist.as_ref().unwrap().Save(wdest.as_ptr(), TRUE);
persist.as_ref().unwrap().Release();
linka.as_ref().unwrap().Release();
CoUninitialize();
}
return Ok(());
}
#[cfg(windows)]
fn emulate_windows_symlink(src: &Path, dst: &Path) -> io::Result<()>
{
use std::fs::File;
use std::io::Write;
use std::ffi::OsString;
use std::fs::canonicalize;
let mut dst1 = OsString::from(dst.as_os_str());
dst1.push(".bat");
let mut f = File::create(&dst1)?;
writeln!(f, "@ECHO OFF")?;
writeln!(f, "")?;
let src1 = canonicalize(src)?;
writeln!(f, "{}", src1.to_string_lossy())?;
return Ok(());
}
fn recursive_copy(src: &Path, dst: &Path) -> io::Result<()>
{
if !src.is_dir()
{
std::fs::copy(src, dst)?;
return Ok(());
}
std::fs::create_dir(dst)?;
let paths = std::fs::read_dir(src)?;
for v in paths
{
let useless = v?;
if useless.file_type()?.is_dir()
{
recursive_copy(&useless.path(), &dst.join(useless.file_name()))?;
}
else
{
std::fs::copy(useless.path(), &dst.join(useless.file_name()))?;
}
}
return Ok(());
}
pub struct InstallInterpreter<'a, TError: Error>
{
interpreter: &'a mut dyn SimpleInterpreter<TError::ErrorType>,
cache: &'a mut Cache<TError>,
props: &'a HashMap<String, String>,
client: Client,
resources: &'a HashMap<&'static str, &'static [u8]>,
content_dir: &'a Path,
bin_dir: &'a Path,
#[cfg(target_os = "linux")]
install_dir: &'a Path,
#[cfg(not(target_os = "macos"))]
method: InstallMethod
}
impl <'a, TError: Error> InstallInterpreter<'a, TError>
{
#[cfg(target_os = "linux")]
pub fn new(interpreter: &'a mut dyn SimpleInterpreter<TError::ErrorType>, cache: &'a mut Cache<TError>, install_dir: &'a Path, method: InstallMethod, content_dir: &'a Path, bin_dir: &'a Path, props: &'a HashMap<String, String>, resources: &'a HashMap<&'static str, &'static [u8]>) -> InstallInterpreter<'a, TError>
{
return InstallInterpreter
{
interpreter: interpreter,
cache: cache,
props: props,
client: Client::new(),
resources: resources,
content_dir: content_dir,
bin_dir: bin_dir,
install_dir: install_dir,
method: method
}
}
#[cfg(target_os = "windows")]
pub fn new(interpreter: &'a mut dyn SimpleInterpreter<TError::ErrorType>, cache: &'a mut Cache<TError>, method: InstallMethod, content_dir: &'a Path, bin_dir: &'a Path, props: &'a HashMap<String, String>, resources: &'a HashMap<&'static str, &'static [u8]>) -> InstallInterpreter<'a, TError>
{
return InstallInterpreter
{
interpreter: interpreter,
cache: cache,
props: props,
client: Client::new(),
resources: resources,
content_dir: content_dir,
bin_dir: bin_dir,
method: method
}
}
#[cfg(target_os = "macos")]
pub fn new(interpreter: &'a mut dyn SimpleInterpreter<TError::ErrorType>, cache: &'a mut Cache<TError>, content_dir: &'a Path, bin_dir: &'a Path, props: &'a HashMap<String, String>, resources: &'a HashMap<&'static str, &'static [u8]>) -> InstallInterpreter<'a, TError>
{
return InstallInterpreter
{
interpreter: interpreter,
cache: cache,
props: props,
client: Client::new(),
resources: resources,
content_dir: content_dir,
bin_dir: bin_dir
}
}
fn download_file(&mut self, resid: usize, filename: String, url: String, headers: HashMap<String, String>) -> Result<(), TError::ErrorType>
{
return base::download_file(self.interpreter, &mut self.client, &mut self.cache, resid, filename, url, headers);
}
fn unpack_cached(&mut self, resid: usize, path: String) -> Result<(), TError::ErrorType>
{
return base::unpack_cached(self.interpreter, &mut self.cache, resid, path);
}
fn extract_resource(&mut self, resid: usize, path: &'static str) -> Result<(), TError::ErrorType>
{
return base::extract_resource(&mut self.cache, &self.resources, resid, path);
}
fn user_input(&mut self, resid: usize, prop: String, message: String) -> Result<(), TError::ErrorType>
{
return base::user_input(self.interpreter, &mut self.cache, &self.props, resid, prop, message);
}
fn create_folder(&mut self, resid: usize, name: String) -> Result<(), TError::ErrorType>
{
let path = self.content_dir.join(name);
if path.exists()
{
self.cache.insert(resid, path);
return Ok(());
}
match std::fs::create_dir(&path)
{
Ok(()) =>
{
self.cache.insert(resid, path);
return Ok(());
},
Err(e) => return Err(TError::io(e))
};
}
fn install_cached(&mut self, resid: usize, path: String, folder: usize) -> Result<(), TError::ErrorType>
{
let src = PathBuf::from(self.cache.parse_string(&path)?);
let dst;
let dstf;
if folder == 0
{
dst = self.content_dir;
}
else
{
dst = Path::new(self.cache.get(folder)?);
}
if let Some(name) = src.file_name()
{
dstf = dst.join(name);
}
else
{
dstf = dst.join(&src);
}
if let Err(e) = recursive_copy(&src, &dstf)
{
return Err(TError::io(e));
}
self.cache.insert(resid, dstf);
return Ok(());
}
fn install_resource(&mut self, resid: usize, path: &'static str, folder: usize) -> Result<(), TError::ErrorType>
{
let src = match self.resources.get(path)
{
Some(v) => v,
None => return Err(TError::unknown_resource_path(path))
};
let dst;
if folder == 0
{
dst = self.content_dir;
}
else
{
dst = Path::new(self.cache.get(folder)?);
}
let name =
{
if let Some(id) = path.rfind('/')
{
&path[id + 1..]
}
else
{
path
}
};
let dstf = dst.join(name);
match base::buf_to_file(&dstf, src)
{
Ok(()) =>
{
self.cache.insert(resid, dstf);
return Ok(());
},
Err(e) => return Err(TError::io(e))
}
}
fn add_to_path(&mut self, resid: usize, path: String) -> Result<(), TError::ErrorType>
{
let src = self.content_dir.join(path);
let name =
{
if let Some(n) = src.file_name()
{
n
}
else
{
src.as_os_str()
}
};
let dst = self.bin_dir.join(name);
if !src.exists()
{
return Err(TError::io(io::Error::new(io::ErrorKind::NotFound, "Could not file input file")));
}
#[cfg(unix)]
{
if let Err(e) = check_and_assign_bin_perm(&src)
{
return Err(TError::io(e));
}
use std::os::unix::fs::symlink;
if let Err(e) = symlink(src, &dst)
{
return Err(TError::io(e));
}
}
#[cfg(windows)]
{
platform::winpath::append_path::<TError>(self.bin_dir, self.method)?;
if let Err(e) = emulate_windows_symlink(&src, &dst)
{
return Err(TError::io(e));
}
}
self.cache.insert(resid, dst);
return Ok(());
}
fn add_shortcut(&mut self, path: String) -> Result<(), TError::ErrorType>
{
#[cfg(target_os="linux")]
{
let dir = platform::common::get_desktop_folder(self.install_dir, self.method);
if let Err(e) = auto_create_desktop(&self.cache.get_path(Path::new("."))?, &dir, self.content_dir, &path)
{
return Err(TError::io(e));
}
}
#[cfg(windows)]
{
let dir = platform::common::get_desktop_folder(self.method);
auto_create_shortcut::<TError>(&dir, self.content_dir, &path)?;
}
return Ok(());
}
}
impl <TError: Error> Interpreter<InstallCommand> for InstallInterpreter<'_, TError>
{
type ErrorType = TError::ErrorType;
fn execute(&mut self, resid: usize, cmd: InstallCommand) -> Result<(), TError::ErrorType>
{
match cmd
{
InstallCommand::DownloadFile(f, u, h) => self.download_file(resid, f, u, h)?,
InstallCommand::UnpackCached(p) => self.unpack_cached(resid, p)?,
InstallCommand::ExtractResource(p) => self.extract_resource(resid, p)?,
InstallCommand::UserInput(p, m) => self.user_input(resid, p, m)?,
InstallCommand::AddToPath(p) => self.add_to_path(resid, p)?,
InstallCommand::CreateFolder(n) => self.create_folder(resid, n)?,
InstallCommand::InstallCached(p, f) => self.install_cached(resid, p, f)?,
InstallCommand::InstallResource(p, f) => self.install_resource(resid, p, f)?,
InstallCommand::AddShortcut(p) => self.add_shortcut(p)?
}
return Ok(());
}
fn progress(&mut self, cur: usize, max: usize) -> Result<(), Self::ErrorType>
{
self.interpreter.update_step((cur as f64 / max as f64) as f32)?;
return Ok(());
}
}