aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com>2023-03-19 21:19:38 +0100
committerGitHub <noreply@github.com>2023-03-19 20:19:38 +0000
commit6c0f600cc6dcb22b3990283aa5d3d7593da6bbb5 (patch)
tree45fb30229c437bc70a1abdceb020400a9ff8dbb8
parent72070a9f6809f679ddd5874edfe454a8d8559092 (diff)
downloadFlightCore-6c0f600cc6dcb22b3990283aa5d3d7593da6bbb5.tar.gz
FlightCore-6c0f600cc6dcb22b3990283aa5d3d7593da6bbb5.zip
refactor: Fix PR downloads permission issue (#217)
* fix: Download PRs into Titanfall2 folder Avoids any folder write permission issues * fix: Download Mod PRs into Titanfall2 folder Avoids any folder write permission issues * build: Add libraries for zip extract * refactor: Download mods zip into memory and extract it from there directly into Titanfall2 dir * fix: Stop creating unneeded temp download folder * refactor: Download launcher zip into memory and extract it from there into a temp folder then only copy out necessary files and delete the rest * docs: Add comment * refactor: Remove unused functions * fix: Smoothly handle misformatted zip file Usually caused by the downloaded file not actually being a zip Catches the error and displays error message if it occurs. * feat: Exit early if download was unsuccessful * chore: Remove leftover commented out code * fix: Write mod files into correct subdirectory Was writing them into parent instead by accident which meant that Northstar wouldn't actually find it. * refactor: Define profile path once Instead of recreating the string everytime
-rw-r--r--src-tauri/Cargo.lock12
-rw-r--r--src-tauri/Cargo.toml2
-rw-r--r--src-tauri/src/github/pull_requests.rs263
3 files changed, 90 insertions, 187 deletions
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 7ecb04df..d1779cf0 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -109,6 +109,7 @@ dependencies = [
"tokio",
"ts-rs",
"zip",
+ "zip-extract",
]
[[package]]
@@ -5037,6 +5038,17 @@ dependencies = [
]
[[package]]
+name = "zip-extract"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb654964c003959ed64cbd0d7b329bcdcbd9690facd50c8617748d3622543972"
+dependencies = [
+ "log",
+ "thiserror",
+ "zip",
+]
+
+[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index fafad444..a6142cea 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -54,6 +54,8 @@ chrono = "0.4.23"
ts-rs = "6.1"
# const formatting
const_format = "0.2.30"
+# Extracting zip files easily
+zip-extract = "0.1.2"
[features]
# by default Tauri runs in production mode
diff --git a/src-tauri/src/github/pull_requests.rs b/src-tauri/src/github/pull_requests.rs
index 2b7be30b..586a4fb3 100644
--- a/src-tauri/src/github/pull_requests.rs
+++ b/src-tauri/src/github/pull_requests.rs
@@ -4,7 +4,6 @@ use anyhow::anyhow;
use app::check_is_valid_game_path;
use app::constants::{APP_USER_AGENT, PULLS_API_ENDPOINT_LAUNCHER, PULLS_API_ENDPOINT_MODS};
use serde::{Deserialize, Serialize};
-use std::fs;
use std::fs::File;
use std::io;
use std::io::prelude::*;
@@ -93,61 +92,6 @@ pub async fn get_pull_requests_wrapper(
get_pull_requests(api_pr_url.to_string()).await
}
-fn unzip(zip_file_name: &str) -> String {
- let fname = std::path::Path::new(zip_file_name);
- let file = fs::File::open(fname).unwrap();
-
- let mut archive = zip::ZipArchive::new(file).unwrap();
-
- let mut folder_name = "".to_string();
-
- for i in 0..archive.len() {
- let mut file = archive.by_index(i).unwrap();
- let outpath = match file.enclosed_name() {
- Some(path) => path.to_owned(),
- None => continue,
- };
-
- {
- let comment = file.comment();
- if !comment.is_empty() {
- println!("File {} comment: {}", i, comment);
- }
- }
-
- if i == 0 {
- // Sanity check that it's a folder
- assert!((*file.name()).ends_with('/'));
-
- folder_name = format!("{}", outpath.display());
- println!("{}", folder_name);
- }
-
- if (*file.name()).ends_with('/') {
- fs::create_dir_all(&outpath).unwrap();
- } else {
- if let Some(p) = outpath.parent() {
- if !p.exists() {
- fs::create_dir_all(p).unwrap();
- }
- }
- let mut outfile = fs::File::create(&outpath).unwrap();
- io::copy(&mut file, &mut outfile).unwrap();
- }
-
- // Get and Set permissions
- #[cfg(unix)]
- {
- use std::os::unix::fs::PermissionsExt;
-
- if let Some(mode) = file.unix_mode() {
- fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap();
- }
- }
- }
- folder_name
-}
-
pub async fn check_github_api(url: &str) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let res = client
@@ -165,99 +109,20 @@ pub async fn check_github_api(url: &str) -> Result<serde_json::Value, Box<dyn st
Ok(json)
}
-/// Downloads a file from given URL
-async fn download_zip(download_url: String, location: String) -> Result<(), anyhow::Error> {
- let client = reqwest::Client::new();
- let resp = client
- .get(download_url)
- .header(reqwest::header::USER_AGENT, APP_USER_AGENT)
- .send()
- .await?;
-
- // Error out earlier if non-successful response
- if !resp.status().is_success() {
- // Return error cause wrong game path
- return Err(anyhow!(
- "Couldn't download zip. Received error code \"{}\"",
- resp.status()
- ));
- }
-
- let mut out = fs::File::create(format!("{}/ns-dev-test-helper-temp-pr-files.zip", location))
- .expect("failed to create file");
- let bytes = resp.bytes().await?;
- let mut cursor = std::io::Cursor::new(bytes);
- std::io::copy(&mut cursor, &mut out)?;
- Ok(())
-}
-
-fn unzip_launcher_zip(zip_file_name: &str) -> String {
- let outfolder_name = "ns-dev-test-helper-temp-pr-files";
- let fname = std::path::Path::new(zip_file_name);
- let file = fs::File::open(fname).unwrap();
-
- let mut archive = zip::ZipArchive::new(file).unwrap();
-
- fs::create_dir_all(outfolder_name).unwrap();
-
- for i in 0..archive.len() {
- let mut file = archive.by_index(i).unwrap();
- let outpath = match file.enclosed_name() {
- Some(path) => path.to_owned(),
- None => continue,
- };
-
- {
- let comment = file.comment();
- if !comment.is_empty() {
- println!("File {} comment: {}", i, comment);
- }
- }
-
- // Only extract two hardcoded files
- if *file.name() == *"NorthstarLauncher.exe" || *file.name() == *"Northstar.dll" {
- println!(
- "File {} extracted to \"{}\" ({} bytes)",
- i,
- outpath.display(),
- file.size()
- );
- if let Some(p) = outpath.parent() {
- if !p.exists() {
- fs::create_dir_all(p).unwrap();
- }
- }
- let mut outfile =
- fs::File::create(format!("{}/{}", outfolder_name, outpath.display())).unwrap();
- std::io::copy(&mut file, &mut outfile).unwrap();
- }
+/// Downloads a file from given URL into an array in memory
+async fn download_zip_into_memory(download_url: String) -> Result<Vec<u8>, anyhow::Error> {
+ let client = reqwest::Client::builder()
+ .user_agent(APP_USER_AGENT)
+ .build()?;
- // Get and Set permissions
- #[cfg(unix)]
- {
- use std::os::unix::fs::PermissionsExt;
+ let response = client.get(download_url).send().await?;
- if let Some(mode) = file.unix_mode() {
- fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap();
- }
- }
+ if !response.status().is_success() {
+ return Err(anyhow!("Request unsuccessful: {}", response.status()));
}
- outfolder_name.to_string()
-}
-/// Recursively copies files from one directory to another
-fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
- fs::create_dir_all(&dst)?;
- for entry in fs::read_dir(src)? {
- let entry = entry?;
- let ty = entry.file_type()?;
- if ty.is_dir() {
- copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
- } else {
- fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
- }
- }
- Ok(())
+ let bytes = response.bytes().await?;
+ Ok(bytes.to_vec())
}
/// Gets GitHub download link of a mods PR
@@ -356,28 +221,64 @@ pub async fn apply_launcher_pr(
check_is_valid_game_path(game_install_path)?;
// get download link
- let download_url = get_launcher_download_link(pull_request).await?;
+ let download_url = get_launcher_download_link(pull_request.clone()).await?;
- // download
- match download_zip(download_url, ".".to_string()).await {
- Ok(_) => (),
+ let archive = match download_zip_into_memory(download_url).await {
+ Ok(archive) => archive,
Err(err) => return Err(err.to_string()),
};
- // extract
- let zip_extract_folder_name = unzip_launcher_zip("ns-dev-test-helper-temp-pr-files.zip");
- fs::remove_file("ns-dev-test-helper-temp-pr-files.zip").unwrap();
-
- // Copy downloaded folder to game install folder
- match copy_dir_all(zip_extract_folder_name.clone(), game_install_path) {
+ let extract_directory = format!(
+ "{}/___flightcore-temp-download-dir/launcher-pr-{}",
+ game_install_path, pull_request.number
+ );
+ match std::fs::create_dir_all(extract_directory.clone()) {
Ok(_) => (),
Err(err) => {
- return Err(format!("Failed copying files: {}", err));
+ return Err(format!(
+ "Failed creating temporary download directory: {}",
+ err
+ ))
+ }
+ };
+
+ let target_dir = std::path::PathBuf::from(extract_directory.clone()); // Doesn't need to exist
+ match zip_extract::extract(io::Cursor::new(archive), &target_dir, true) {
+ Ok(()) => (),
+ Err(err) => {
+ return Err(format!("Failed unzip: {}", err));
}
+ };
+
+ // Copy only necessary files from temp dir
+ // Copy:
+ // - NorthstarLauncher.exe
+ // - Northstar.dll
+ let files_to_copy = vec!["NorthstarLauncher.exe", "Northstar.dll"];
+ for file_name in files_to_copy {
+ let source_file_path = format!("{}/{}", extract_directory, file_name);
+ let destination_file_path = format!("{}/{}", game_install_path, file_name);
+ match std::fs::copy(&source_file_path, &destination_file_path) {
+ Ok(_result) => (),
+ Err(err) => {
+ return Err(format!(
+ "Failed to copy necessary file {} from temp dir: {}",
+ file_name, err
+ ))
+ }
+ };
}
- // Delete old unzipped
- fs::remove_dir_all(zip_extract_folder_name).unwrap();
+ // delete extract directory
+ match std::fs::remove_dir_all(&extract_directory) {
+ Ok(()) => (),
+ Err(err) => {
+ return Err(format!(
+ "Failed to delete temporary download directory: {}",
+ err
+ ))
+ }
+ }
println!("All done with installing launcher PR");
Ok(())
@@ -397,47 +298,35 @@ pub async fn apply_mods_pr(
Err(err) => return Err(err.to_string()),
};
- match download_zip(download_url, ".".to_string()).await {
- Ok(()) => (),
+ let archive = match download_zip_into_memory(download_url).await {
+ Ok(archive) => archive,
Err(err) => return Err(err.to_string()),
};
- // Extract folder and delete zip
- let zip_extract_folder_name = unzip("ns-dev-test-helper-temp-pr-files.zip");
- fs::remove_file("ns-dev-test-helper-temp-pr-files.zip").unwrap();
+ let profile_folder = format!("{}/R2Northstar-PR-test-managed-folder", game_install_path);
// Delete previously managed folder
- if std::fs::remove_dir_all(format!(
- "{}/R2Northstar-PR-test-managed-folder",
- game_install_path
- ))
- .is_err()
- {
- if std::path::Path::new(&format!(
- "{}/R2Northstar-PR-test-managed-folder",
- game_install_path
- ))
- .exists()
- {
+ if std::fs::remove_dir_all(profile_folder.clone()).is_err() {
+ if std::path::Path::new(&profile_folder).exists() {
println!("Failed removing previous dir");
} else {
println!("Failed removing folder that doesn't exist. Probably cause first run");
}
};
- // Copy downloaded folder to game install folder
- copy_dir_all(
- zip_extract_folder_name.clone(),
- format!(
- "{}/R2Northstar-PR-test-managed-folder/mods",
- game_install_path
- ),
- )
- .unwrap();
-
- // Delete old copy
- std::fs::remove_dir_all(zip_extract_folder_name).unwrap();
+ // Create profile folder
+ match std::fs::create_dir_all(profile_folder.clone()) {
+ Ok(()) => (),
+ Err(err) => return Err(err.to_string()),
+ }
+ let target_dir = std::path::PathBuf::from(format!("{}/mods", profile_folder)); // Doesn't need to exist
+ match zip_extract::extract(io::Cursor::new(archive), &target_dir, true) {
+ Ok(()) => (),
+ Err(err) => {
+ return Err(format!("Failed unzip: {}", err));
+ }
+ };
// Add batch file to launch right profile
add_batch_file(game_install_path);