util.rs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use std::{
  5. fs::{create_dir_all, File},
  6. io::{Cursor, Read, Write},
  7. path::{Path, PathBuf},
  8. };
  9. use log::info;
  10. use sha2::Digest;
  11. use zip::ZipArchive;
  12. pub const WEBVIEW2_BOOTSTRAPPER_URL: &str = "https://go.microsoft.com/fwlink/p/?LinkId=2124703";
  13. pub const WEBVIEW2_OFFLINE_INSTALLER_X86_URL: &str =
  14. "https://go.microsoft.com/fwlink/?linkid=2099617";
  15. pub const WEBVIEW2_OFFLINE_INSTALLER_X64_URL: &str =
  16. "https://go.microsoft.com/fwlink/?linkid=2124701";
  17. pub const WEBVIEW2_URL_PREFIX: &str =
  18. "https://msedge.sf.dl.delivery.mp.microsoft.com/filestreamingservice/files/";
  19. pub const NSIS_OUTPUT_FOLDER_NAME: &str = "nsis";
  20. pub const NSIS_UPDATER_OUTPUT_FOLDER_NAME: &str = "nsis-updater";
  21. pub const WIX_OUTPUT_FOLDER_NAME: &str = "msi";
  22. pub const WIX_UPDATER_OUTPUT_FOLDER_NAME: &str = "msi-updater";
  23. pub fn webview2_guid_path(url: &str) -> crate::Result<(String, String)> {
  24. let agent = ureq::AgentBuilder::new().try_proxy_from_env(true).build();
  25. let response = agent.head(url).call().map_err(Box::new)?;
  26. let final_url = response.get_url();
  27. let remaining_url = final_url.strip_prefix(WEBVIEW2_URL_PREFIX).ok_or_else(|| {
  28. anyhow::anyhow!(
  29. "WebView2 URL prefix mismatch. Expected `{}`, found `{}`.",
  30. WEBVIEW2_URL_PREFIX,
  31. final_url
  32. )
  33. })?;
  34. let (guid, filename) = remaining_url.split_once('/').ok_or_else(|| {
  35. anyhow::anyhow!(
  36. "WebView2 URL format mismatch. Expected `<GUID>/<FILENAME>`, found `{}`.",
  37. remaining_url
  38. )
  39. })?;
  40. Ok((guid.into(), filename.into()))
  41. }
  42. pub fn download_webview2_bootstrapper(base_path: &Path) -> crate::Result<PathBuf> {
  43. let file_path = base_path.join("MicrosoftEdgeWebview2Setup.exe");
  44. if !file_path.exists() {
  45. std::fs::write(&file_path, download(WEBVIEW2_BOOTSTRAPPER_URL)?)?;
  46. }
  47. Ok(file_path)
  48. }
  49. pub fn download_webview2_offline_installer(base_path: &Path, arch: &str) -> crate::Result<PathBuf> {
  50. let url = if arch == "x64" {
  51. WEBVIEW2_OFFLINE_INSTALLER_X64_URL
  52. } else {
  53. WEBVIEW2_OFFLINE_INSTALLER_X86_URL
  54. };
  55. let (guid, filename) = webview2_guid_path(url)?;
  56. let dir_path = base_path.join(guid);
  57. let file_path = dir_path.join(filename);
  58. if !file_path.exists() {
  59. create_dir_all(dir_path)?;
  60. std::fs::write(&file_path, download(url)?)?;
  61. }
  62. Ok(file_path)
  63. }
  64. pub fn download(url: &str) -> crate::Result<Vec<u8>> {
  65. info!(action = "Downloading"; "{}", url);
  66. let agent = ureq::AgentBuilder::new().try_proxy_from_env(true).build();
  67. let response = agent.get(url).call().map_err(Box::new)?;
  68. let mut bytes = Vec::new();
  69. response.into_reader().read_to_end(&mut bytes)?;
  70. Ok(bytes)
  71. }
  72. pub enum HashAlgorithm {
  73. #[cfg(target_os = "windows")]
  74. Sha256,
  75. Sha1,
  76. }
  77. /// Function used to download a file and checks SHA256 to verify the download.
  78. pub fn download_and_verify(
  79. url: &str,
  80. hash: &str,
  81. hash_algorithm: HashAlgorithm,
  82. ) -> crate::Result<Vec<u8>> {
  83. let data = download(url)?;
  84. info!("validating hash");
  85. match hash_algorithm {
  86. #[cfg(target_os = "windows")]
  87. HashAlgorithm::Sha256 => {
  88. let hasher = sha2::Sha256::new();
  89. verify(&data, hash, hasher)?;
  90. }
  91. HashAlgorithm::Sha1 => {
  92. let hasher = sha1::Sha1::new();
  93. verify(&data, hash, hasher)?;
  94. }
  95. }
  96. Ok(data)
  97. }
  98. fn verify(data: &Vec<u8>, hash: &str, mut hasher: impl Digest) -> crate::Result<()> {
  99. hasher.update(data);
  100. let url_hash = hasher.finalize().to_vec();
  101. let expected_hash = hex::decode(hash)?;
  102. if expected_hash == url_hash {
  103. Ok(())
  104. } else {
  105. Err(crate::Error::HashError)
  106. }
  107. }
  108. /// Extracts the zips from memory into a useable path.
  109. pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> {
  110. let cursor = Cursor::new(data);
  111. let mut zipa = ZipArchive::new(cursor)?;
  112. for i in 0..zipa.len() {
  113. let mut file = zipa.by_index(i)?;
  114. if let Some(name) = file.enclosed_name() {
  115. let dest_path = path.join(name);
  116. if file.is_dir() {
  117. create_dir_all(&dest_path)?;
  118. continue;
  119. }
  120. let parent = dest_path.parent().expect("Failed to get parent");
  121. if !parent.exists() {
  122. create_dir_all(parent)?;
  123. }
  124. let mut buff: Vec<u8> = Vec::new();
  125. file.read_to_end(&mut buff)?;
  126. let mut fileout = File::create(dest_path).expect("Failed to open file");
  127. fileout.write_all(&buff)?;
  128. }
  129. }
  130. Ok(())
  131. }
  132. #[cfg(target_os = "windows")]
  133. pub fn os_bitness<'a>() -> Option<&'a str> {
  134. use windows_sys::Win32::System::{
  135. Diagnostics::Debug::{PROCESSOR_ARCHITECTURE_AMD64, PROCESSOR_ARCHITECTURE_INTEL},
  136. SystemInformation::{GetNativeSystemInfo, SYSTEM_INFO},
  137. };
  138. let mut system_info: SYSTEM_INFO = unsafe { std::mem::zeroed() };
  139. unsafe { GetNativeSystemInfo(&mut system_info) };
  140. match unsafe { system_info.Anonymous.Anonymous.wProcessorArchitecture } {
  141. PROCESSOR_ARCHITECTURE_INTEL => Some("x86"),
  142. PROCESSOR_ARCHITECTURE_AMD64 => Some("x64"),
  143. _ => None,
  144. }
  145. }