Forráskód Böngészése

refactor(core): load APPIMAGE and APPDIR env vars on startup [TRI-007] [TRI-041]

Lucas Nogueira 3 éve
szülő
commit
7209fdf732

+ 5 - 0
.changes/core-env.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+The `process`, `path` and `updater` APIs now takes a `tauri::Env` argument, used to force environment variables load on startup to prevent env var update attacks.

+ 22 - 0
core/tauri-utils/src/lib.rs

@@ -37,6 +37,28 @@ impl PackageInfo {
   }
 }
 
+/// Information about environment variables.
+#[derive(Debug, Clone)]
+pub struct Env {
+  /// The APPIMAGE environment variable.
+  #[cfg(target_os = "linux")]
+  pub appimage: Option<std::ffi::OsString>,
+  /// The APPDIR environment variable.
+  #[cfg(target_os = "linux")]
+  pub appdir: Option<std::ffi::OsString>,
+}
+
+impl Default for Env {
+  fn default() -> Self {
+    Self {
+      #[cfg(target_os = "linux")]
+      appimage: std::env::var_os("APPIMAGE"),
+      #[cfg(target_os = "linux")]
+      appdir: std::env::var_os("APPDIR"),
+    }
+  }
+}
+
 /// The result type of `tauri-utils`.
 pub type Result<T> = std::result::Result<T, Error>;
 

+ 6 - 8
core/tauri-utils/src/platform.rs

@@ -4,12 +4,9 @@
 
 //! Platform helper functions.
 
-use std::{
-  env,
-  path::{PathBuf, MAIN_SEPARATOR},
-};
+use std::path::{PathBuf, MAIN_SEPARATOR};
 
-use crate::PackageInfo;
+use crate::{Env, PackageInfo};
 
 /// Try to determine the current target triple.
 ///
@@ -76,7 +73,7 @@ pub fn target_triple() -> crate::Result<String> {
 /// `${exe_dir}/../lib/${exe_name}`.
 ///
 /// On MacOS, it's `${exe_dir}../Resources` (inside .app).
-pub fn resource_dir(package_info: &PackageInfo) -> crate::Result<PathBuf> {
+pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result<PathBuf> {
   let exe = std::env::current_exe()?;
   let exe_dir = exe.parent().expect("failed to get exe directory");
   let curr_dir = exe_dir.display().to_string();
@@ -93,10 +90,11 @@ pub fn resource_dir(package_info: &PackageInfo) -> crate::Result<PathBuf> {
     if curr_dir.ends_with("/data/usr/bin") {
       // running from the deb bundle dir
       Ok(exe_dir.join(format!("../lib/{}", package_info.package_name())))
-    } else if let Ok(appdir) = env::var("APPDIR") {
+    } else if let Some(appdir) = &env.appdir {
+      let appdir: &std::path::Path = appdir.as_ref();
       Ok(PathBuf::from(format!(
         "{}/usr/lib/{}",
-        appdir,
+        appdir.display(),
         package_info.package_name()
       )))
     } else {

+ 6 - 4
core/tauri/src/api/path.rs

@@ -9,7 +9,7 @@ use std::{
   path::{Component, Path, PathBuf},
 };
 
-use crate::{Config, PackageInfo};
+use crate::{Config, Env, PackageInfo};
 
 use serde_repr::{Deserialize_repr, Serialize_repr};
 
@@ -83,6 +83,7 @@ pub enum BaseDirectory {
 ///     authors: "tauri",
 ///     description: "a tauri test",
 ///   },
+///   &Default::default(),
 ///   "path/to/something",
 ///   Some(BaseDirectory::Config)
 ///  ).expect("failed to resolve path");
@@ -91,6 +92,7 @@ pub enum BaseDirectory {
 pub fn resolve_path<P: AsRef<Path>>(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: P,
   dir: Option<BaseDirectory>,
 ) -> crate::api::Result<PathBuf> {
@@ -113,7 +115,7 @@ pub fn resolve_path<P: AsRef<Path>>(
       BaseDirectory::Runtime => runtime_dir(),
       BaseDirectory::Template => template_dir(),
       BaseDirectory::Video => video_dir(),
-      BaseDirectory::Resource => resource_dir(package_info),
+      BaseDirectory::Resource => resource_dir(package_info, env),
       BaseDirectory::App => app_dir(config),
       BaseDirectory::Current => Some(env::current_dir()?),
       BaseDirectory::Log => log_dir(config),
@@ -229,8 +231,8 @@ pub fn video_dir() -> Option<PathBuf> {
 }
 
 /// Returns the path to the resource directory of this app.
-pub fn resource_dir(package_info: &PackageInfo) -> Option<PathBuf> {
-  crate::utils::platform::resource_dir(package_info).ok()
+pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> Option<PathBuf> {
+  crate::utils::platform::resource_dir(package_info, env).ok()
 }
 
 /// Returns the path to the suggested directory for your app config files.

+ 7 - 4
core/tauri/src/api/process.rs

@@ -4,6 +4,8 @@
 
 //! Types and functions related to child processes management.
 
+use crate::Env;
+
 use std::{
   env,
   path::PathBuf,
@@ -16,12 +18,13 @@ mod command;
 pub use command::*;
 
 /// Gets the current binary.
-pub fn current_binary() -> Option<PathBuf> {
+#[allow(unused_variables)]
+pub fn current_binary(env: &Env) -> Option<PathBuf> {
   let mut current_binary = None;
 
   // if we are running with an APP Image, we should return the app image path
   #[cfg(target_os = "linux")]
-  if let Some(app_image_path) = env::var_os("APPIMAGE") {
+  if let Some(app_image_path) = &env.appimage {
     current_binary = Some(PathBuf::from(app_image_path));
   }
 
@@ -37,8 +40,8 @@ pub fn current_binary() -> Option<PathBuf> {
 }
 
 /// Restarts the process.
-pub fn restart() {
-  if let Some(path) = current_binary() {
+pub fn restart(env: &Env) {
+  if let Some(path) = current_binary(env) {
     StdCommand::new(path)
       .spawn()
       .expect("application failed to start");

+ 11 - 2
core/tauri/src/app.rs

@@ -19,8 +19,8 @@ use crate::{
     Dispatch, ExitRequestedEventAction, RunEvent, Runtime,
   },
   sealed::{ManagerBase, RuntimeOrDispatch},
-  utils::assets::Assets,
   utils::config::{Config, WindowUrl},
+  utils::{assets::Assets, Env},
   Context, Invoke, InvokeError, InvokeResponse, Manager, StateManager, Window,
 };
 
@@ -150,6 +150,7 @@ impl<R: Runtime> GlobalWindowEvent<R> {
 /// The path resolver is a helper for the application-specific [`crate::api::path`] APIs.
 #[derive(Debug, Clone)]
 pub struct PathResolver {
+  env: Env,
   config: Arc<Config>,
   package_info: PackageInfo,
 }
@@ -157,7 +158,7 @@ pub struct PathResolver {
 impl PathResolver {
   /// Returns the path to the resource directory of this app.
   pub fn resource_dir(&self) -> Option<PathBuf> {
-    crate::api::path::resource_dir(&self.package_info)
+    crate::api::path::resource_dir(&self.package_info, &self.env)
   }
 
   /// Returns the path to the suggested directory for your app config files.
@@ -407,6 +408,7 @@ macro_rules! shared_app_impl {
       /// The path resolver for the application.
       pub fn path_resolver(&self) -> PathResolver {
         PathResolver {
+          env: self.state::<Env>().inner().clone(),
           config: self.manager.config(),
           package_info: self.manager.package_info().clone(),
         }
@@ -432,6 +434,11 @@ macro_rules! shared_app_impl {
         self.manager.package_info()
       }
 
+      /// Gets the managed [`Env`].
+      pub fn env(&self) -> Env {
+        self.state::<Env>().inner().clone()
+      }
+
       /// The application's asset resolver.
       pub fn asset_resolver(&self) -> AssetResolver<R> {
         AssetResolver {
@@ -990,6 +997,8 @@ impl<R: Runtime> Builder<R> {
       },
     };
 
+    app.manage(Env::default());
+
     #[cfg(feature = "system-tray")]
     if let Some(system_tray) = self.system_tray {
       let mut ids = HashMap::new();

+ 8 - 4
core/tauri/src/endpoints.rs

@@ -75,17 +75,21 @@ impl Module {
           .and_then(|r| r.json)
           .map_err(InvokeError::from)
       }),
-      Self::Process(cmd) => resolver
-        .respond_async(async move { cmd.run().and_then(|r| r.json).map_err(InvokeError::from) }),
+      Self::Process(cmd) => resolver.respond_async(async move {
+        cmd
+          .run(window)
+          .and_then(|r| r.json)
+          .map_err(InvokeError::from)
+      }),
       Self::Fs(cmd) => resolver.respond_async(async move {
         cmd
-          .run(config, &package_info)
+          .run(window, config, &package_info)
           .and_then(|r| r.json)
           .map_err(InvokeError::from)
       }),
       Self::Path(cmd) => resolver.respond_async(async move {
         cmd
-          .run(config, &package_info)
+          .run(window, config, &package_info)
           .and_then(|r| r.json)
           .map_err(InvokeError::from)
       }),

+ 40 - 21
core/tauri/src/endpoints/file_system.rs

@@ -8,7 +8,7 @@ use crate::{
     dir, file,
     path::{resolve_path, BaseDirectory},
   },
-  Config, PackageInfo,
+  Config, Env, Manager, PackageInfo, Runtime, Window,
 };
 
 use serde::{Deserialize, Serialize};
@@ -97,15 +97,17 @@ pub enum Cmd {
 
 impl Cmd {
   #[allow(unused_variables)]
-  pub fn run(
+  pub fn run<R: Runtime>(
     self,
+    window: Window<R>,
     config: Arc<Config>,
     package_info: &PackageInfo,
   ) -> crate::Result<InvokeResponse> {
+    let env = window.state::<Env>().inner();
     match self {
       #[cfg(fs_read_text_file)]
       Self::ReadTextFile { path, options } => {
-        read_text_file(&config, package_info, path, options).map(Into::into)
+        read_text_file(&config, package_info, env, path, options).map(Into::into)
       }
       #[cfg(not(fs_read_text_file))]
       Self::ReadTextFile { .. } => Err(crate::Error::ApiNotAllowlisted(
@@ -114,7 +116,7 @@ impl Cmd {
 
       #[cfg(fs_read_binary_file)]
       Self::ReadBinaryFile { path, options } => {
-        read_binary_file(&config, package_info, path, options).map(Into::into)
+        read_binary_file(&config, package_info, env, path, options).map(Into::into)
       }
       #[cfg(not(fs_read_binary_file))]
       Self::ReadBinaryFile { .. } => Err(crate::Error::ApiNotAllowlisted(
@@ -126,7 +128,7 @@ impl Cmd {
         path,
         contents,
         options,
-      } => write_file(&config, package_info, path, contents, options).map(Into::into),
+      } => write_file(&config, package_info, env, path, contents, options).map(Into::into),
       #[cfg(not(fs_write_file))]
       Self::WriteFile { .. } => Err(crate::Error::ApiNotAllowlisted(
         "fs > writeFile".to_string(),
@@ -137,7 +139,7 @@ impl Cmd {
         path,
         contents,
         options,
-      } => write_binary_file(&config, package_info, path, contents, options).map(Into::into),
+      } => write_binary_file(&config, package_info, env, path, contents, options).map(Into::into),
       #[cfg(not(fs_write_binary_file))]
       Self::WriteBinaryFile { .. } => Err(crate::Error::ApiNotAllowlisted(
         "writeBinaryFile".to_string(),
@@ -145,7 +147,7 @@ impl Cmd {
 
       #[cfg(fs_read_dir)]
       Self::ReadDir { path, options } => {
-        read_dir(&config, package_info, path, options).map(Into::into)
+        read_dir(&config, package_info, env, path, options).map(Into::into)
       }
       #[cfg(not(fs_read_dir))]
       Self::ReadDir { .. } => Err(crate::Error::ApiNotAllowlisted("fs > readDir".to_string())),
@@ -155,13 +157,13 @@ impl Cmd {
         source,
         destination,
         options,
-      } => copy_file(&config, package_info, source, destination, options).map(Into::into),
+      } => copy_file(&config, package_info, env, source, destination, options).map(Into::into),
       #[cfg(not(fs_copy_file))]
       Self::CopyFile { .. } => Err(crate::Error::ApiNotAllowlisted("fs > copyFile".to_string())),
 
       #[cfg(fs_create_dir)]
       Self::CreateDir { path, options } => {
-        create_dir(&config, package_info, path, options).map(Into::into)
+        create_dir(&config, package_info, env, path, options).map(Into::into)
       }
       #[cfg(not(fs_create_dir))]
       Self::CreateDir { .. } => Err(crate::Error::ApiNotAllowlisted(
@@ -170,7 +172,7 @@ impl Cmd {
 
       #[cfg(fs_remove_dir)]
       Self::RemoveDir { path, options } => {
-        remove_dir(&config, package_info, path, options).map(Into::into)
+        remove_dir(&config, package_info, env, path, options).map(Into::into)
       }
       #[cfg(not(fs_remove_dir))]
       Self::RemoveDir { .. } => Err(crate::Error::ApiNotAllowlisted(
@@ -179,7 +181,7 @@ impl Cmd {
 
       #[cfg(fs_remove_file)]
       Self::RemoveFile { path, options } => {
-        remove_file(&config, package_info, path, options).map(Into::into)
+        remove_file(&config, package_info, env, path, options).map(Into::into)
       }
       #[cfg(not(fs_remove_file))]
       Self::RemoveFile { .. } => Err(crate::Error::ApiNotAllowlisted(
@@ -191,7 +193,7 @@ impl Cmd {
         old_path,
         new_path,
         options,
-      } => rename_file(&config, package_info, old_path, new_path, options).map(Into::into),
+      } => rename_file(&config, package_info, env, old_path, new_path, options).map(Into::into),
       #[cfg(not(fs_rename_file))]
       Self::RenameFile { .. } => Err(crate::Error::ApiNotAllowlisted(
         "fs > renameFile".to_string(),
@@ -205,6 +207,7 @@ impl Cmd {
 pub fn read_dir(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: PathBuf,
   options: Option<DirOperationOptions>,
 ) -> crate::Result<Vec<dir::DiskEntry>> {
@@ -213,8 +216,11 @@ pub fn read_dir(
   } else {
     (false, None)
   };
-  dir::read_dir(resolve_path(config, package_info, path, dir)?, recursive)
-    .map_err(crate::Error::FailedToExecuteApi)
+  dir::read_dir(
+    resolve_path(config, package_info, env, path, dir)?,
+    recursive,
+  )
+  .map_err(crate::Error::FailedToExecuteApi)
 }
 
 /// Copies a file.
@@ -222,14 +228,15 @@ pub fn read_dir(
 pub fn copy_file(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   source: PathBuf,
   destination: PathBuf,
   options: Option<FileOperationOptions>,
 ) -> crate::Result<()> {
   let (src, dest) = match options.and_then(|o| o.dir) {
     Some(dir) => (
-      resolve_path(config, package_info, source, Some(dir.clone()))?,
-      resolve_path(config, package_info, destination, Some(dir))?,
+      resolve_path(config, package_info, env, source, Some(dir.clone()))?,
+      resolve_path(config, package_info, env, destination, Some(dir))?,
     ),
     None => (source, destination),
   };
@@ -242,6 +249,7 @@ pub fn copy_file(
 pub fn create_dir(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: PathBuf,
   options: Option<DirOperationOptions>,
 ) -> crate::Result<()> {
@@ -250,7 +258,7 @@ pub fn create_dir(
   } else {
     (false, None)
   };
-  let resolved_path = resolve_path(config, package_info, path, dir)?;
+  let resolved_path = resolve_path(config, package_info, env, path, dir)?;
   if recursive {
     fs::create_dir_all(resolved_path)?;
   } else {
@@ -265,6 +273,7 @@ pub fn create_dir(
 pub fn remove_dir(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: PathBuf,
   options: Option<DirOperationOptions>,
 ) -> crate::Result<()> {
@@ -273,7 +282,7 @@ pub fn remove_dir(
   } else {
     (false, None)
   };
-  let resolved_path = resolve_path(config, package_info, path, dir)?;
+  let resolved_path = resolve_path(config, package_info, env, path, dir)?;
   if recursive {
     fs::remove_dir_all(resolved_path)?;
   } else {
@@ -288,10 +297,11 @@ pub fn remove_dir(
 pub fn remove_file(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: PathBuf,
   options: Option<FileOperationOptions>,
 ) -> crate::Result<()> {
-  let resolved_path = resolve_path(config, package_info, path, options.and_then(|o| o.dir))?;
+  let resolved_path = resolve_path(config, package_info, env, path, options.and_then(|o| o.dir))?;
   fs::remove_file(resolved_path)?;
   Ok(())
 }
@@ -301,14 +311,15 @@ pub fn remove_file(
 pub fn rename_file(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   old_path: PathBuf,
   new_path: PathBuf,
   options: Option<FileOperationOptions>,
 ) -> crate::Result<()> {
   let (old, new) = match options.and_then(|o| o.dir) {
     Some(dir) => (
-      resolve_path(config, package_info, old_path, Some(dir.clone()))?,
-      resolve_path(config, package_info, new_path, Some(dir))?,
+      resolve_path(config, package_info, env, old_path, Some(dir.clone()))?,
+      resolve_path(config, package_info, env, new_path, Some(dir))?,
     ),
     None => (old_path, new_path),
   };
@@ -320,6 +331,7 @@ pub fn rename_file(
 pub fn write_file(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: PathBuf,
   contents: String,
   options: Option<FileOperationOptions>,
@@ -327,6 +339,7 @@ pub fn write_file(
   File::create(resolve_path(
     config,
     package_info,
+    env,
     path,
     options.and_then(|o| o.dir),
   )?)
@@ -340,6 +353,7 @@ pub fn write_file(
 pub fn write_binary_file(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: PathBuf,
   contents: String,
   options: Option<FileOperationOptions>,
@@ -350,6 +364,7 @@ pub fn write_binary_file(
       File::create(resolve_path(
         config,
         package_info,
+        env,
         path,
         options.and_then(|o| o.dir),
       )?)
@@ -364,12 +379,14 @@ pub fn write_binary_file(
 pub fn read_text_file(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: PathBuf,
   options: Option<FileOperationOptions>,
 ) -> crate::Result<String> {
   file::read_string(resolve_path(
     config,
     package_info,
+    env,
     path,
     options.and_then(|o| o.dir),
   )?)
@@ -381,12 +398,14 @@ pub fn read_text_file(
 pub fn read_binary_file(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: PathBuf,
   options: Option<FileOperationOptions>,
 ) -> crate::Result<Vec<u8>> {
   file::read_binary(resolve_path(
     config,
     package_info,
+    env,
     path,
     options.and_then(|o| o.dir),
   )?)

+ 14 - 6
core/tauri/src/endpoints/notification.rs

@@ -6,7 +6,7 @@ use super::InvokeResponse;
 use serde::Deserialize;
 
 #[cfg(notification_all)]
-use crate::api::notification::Notification;
+use crate::{api::notification::Notification, Env, Manager};
 use crate::{Config, PackageInfo, Runtime, Window};
 
 use std::sync::Arc;
@@ -55,7 +55,7 @@ impl Cmd {
       Self::Notification { .. } => Err(crate::Error::ApiNotAllowlisted("notification".to_string())),
       Self::IsNotificationPermissionGranted => {
         #[cfg(notification_all)]
-        return is_permission_granted(&config, package_info).map(Into::into);
+        return is_permission_granted(&window, &config, package_info).map(Into::into);
         #[cfg(not(notification_all))]
         Ok(false.into())
       }
@@ -84,11 +84,13 @@ pub fn send(options: NotificationOptions, config: &Config) -> crate::Result<Invo
 }
 
 #[cfg(notification_all)]
-pub fn is_permission_granted(
+pub fn is_permission_granted<R: Runtime>(
+  window: &Window<R>,
   config: &Config,
   package_info: &PackageInfo,
 ) -> crate::Result<InvokeResponse> {
-  let settings = crate::settings::read_settings(config, package_info);
+  let settings =
+    crate::settings::read_settings(config, package_info, window.state::<Env>().inner());
   if let Some(allow_notification) = settings.allow_notification {
     Ok(allow_notification.into())
   } else {
@@ -102,7 +104,8 @@ pub fn request_permission<R: Runtime>(
   config: &Config,
   package_info: &PackageInfo,
 ) -> crate::Result<String> {
-  let mut settings = crate::settings::read_settings(config, package_info);
+  let mut settings =
+    crate::settings::read_settings(config, package_info, window.state::<Env>().inner());
   if let Some(allow_notification) = settings.allow_notification {
     return Ok(if allow_notification {
       PERMISSION_GRANTED.to_string()
@@ -123,7 +126,12 @@ pub fn request_permission<R: Runtime>(
   let answer = rx.recv().unwrap();
 
   settings.allow_notification = Some(answer);
-  crate::settings::write_settings(config, package_info, settings)?;
+  crate::settings::write_settings(
+    config,
+    package_info,
+    window.state::<Env>().inner(),
+    settings,
+  )?;
 
   if answer {
     Ok(PERMISSION_GRANTED.to_string())

+ 15 - 6
core/tauri/src/endpoints/path.rs

@@ -3,7 +3,9 @@
 // SPDX-License-Identifier: MIT
 
 use super::InvokeResponse;
-use crate::{api::path::BaseDirectory, Config, PackageInfo};
+use crate::{api::path::BaseDirectory, Config, PackageInfo, Runtime, Window};
+#[cfg(path_all)]
+use crate::{Env, Manager};
 use serde::Deserialize;
 #[cfg(path_all)]
 use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
@@ -42,16 +44,22 @@ pub enum Cmd {
 
 impl Cmd {
   #[allow(unused_variables)]
-  pub fn run(
+  pub fn run<R: Runtime>(
     self,
+    window: Window<R>,
     config: Arc<Config>,
     package_info: &PackageInfo,
   ) -> crate::Result<InvokeResponse> {
     #[cfg(path_all)]
     return match self {
-      Cmd::ResolvePath { directory, path } => {
-        resolve_path_handler(&config, package_info, path, directory).map(Into::into)
-      }
+      Cmd::ResolvePath { directory, path } => resolve_path_handler(
+        &config,
+        package_info,
+        window.state::<Env>().inner(),
+        path,
+        directory,
+      )
+      .map(Into::into),
       Cmd::Resolve { paths } => resolve(paths).map(Into::into),
       Cmd::Normalize { path } => normalize(path).map(Into::into),
       Cmd::Join { paths } => join(paths).map(Into::into),
@@ -69,10 +77,11 @@ impl Cmd {
 pub fn resolve_path_handler(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   path: String,
   directory: Option<BaseDirectory>,
 ) -> crate::Result<PathBuf> {
-  crate::api::path::resolve_path(config, package_info, path, directory).map_err(Into::into)
+  crate::api::path::resolve_path(config, package_info, env, path, directory).map_err(Into::into)
 }
 
 #[cfg(path_all)]

+ 3 - 3
core/tauri/src/endpoints/process.rs

@@ -5,7 +5,7 @@
 use std::process::exit;
 
 use super::InvokeResponse;
-use crate::api::process::restart;
+use crate::{api::process::restart, Manager, Runtime, Window};
 use serde::Deserialize;
 
 /// The API descriptor.
@@ -20,10 +20,10 @@ pub enum Cmd {
 }
 
 impl Cmd {
-  pub fn run(self) -> crate::Result<InvokeResponse> {
+  pub fn run<R: Runtime>(self, window: Window<R>) -> crate::Result<InvokeResponse> {
     match self {
       Self::Relaunch => Ok({
-        restart();
+        restart(&window.state());
         ().into()
       }),
       Self::Exit { exit_code } => {

+ 1 - 1
core/tauri/src/lib.rs

@@ -108,7 +108,7 @@ pub use {
   self::utils::{
     assets::Assets,
     config::{Config, WindowUrl},
-    PackageInfo,
+    Env, PackageInfo,
   },
   self::window::{Monitor, Window},
 };

+ 11 - 5
core/tauri/src/settings.rs

@@ -11,7 +11,7 @@ use crate::{
     file::read_binary,
     path::{resolve_path, BaseDirectory},
   },
-  Config, PackageInfo,
+  Config, Env, PackageInfo,
 };
 use serde::{Deserialize, Serialize};
 use std::{
@@ -30,10 +30,15 @@ pub struct Settings {
 }
 
 /// Gets the path to the settings file.
-fn get_settings_path(config: &Config, package_info: &PackageInfo) -> crate::api::Result<PathBuf> {
+fn get_settings_path(
+  config: &Config,
+  package_info: &PackageInfo,
+  env: &Env,
+) -> crate::api::Result<PathBuf> {
   resolve_path(
     config,
     package_info,
+    env,
     ".tauri-settings",
     Some(BaseDirectory::App),
   )
@@ -44,9 +49,10 @@ fn get_settings_path(config: &Config, package_info: &PackageInfo) -> crate::api:
 pub(crate) fn write_settings(
   config: &Config,
   package_info: &PackageInfo,
+  env: &Env,
   settings: Settings,
 ) -> crate::Result<()> {
-  let settings_path = get_settings_path(config, package_info)?;
+  let settings_path = get_settings_path(config, package_info, env)?;
   let settings_folder = Path::new(&settings_path).parent().unwrap();
   if !settings_folder.exists() {
     std::fs::create_dir(settings_folder)?;
@@ -60,8 +66,8 @@ pub(crate) fn write_settings(
 }
 
 /// Reads the settings from the file system.
-pub fn read_settings(config: &Config, package_info: &PackageInfo) -> Settings {
-  if let Ok(settings_path) = get_settings_path(config, package_info) {
+pub fn read_settings(config: &Config, package_info: &PackageInfo, env: &Env) -> Settings {
+  if let Ok(settings_path) = get_settings_path(config, package_info, env) {
     if settings_path.exists() {
       read_binary(settings_path)
         .and_then(|settings| bincode::deserialize(&settings).map_err(Into::into))

+ 34 - 30
core/tauri/src/updater/core.rs

@@ -3,7 +3,10 @@
 // SPDX-License-Identifier: MIT
 
 use super::error::{Error, Result};
-use crate::api::{file::Extract, version};
+use crate::{
+  api::{file::Extract, version},
+  Env,
+};
 use base64::decode;
 use http::StatusCode;
 use minisign_verify::{PublicKey, Signature};
@@ -171,6 +174,8 @@ impl RemoteRelease {
 
 #[derive(Debug)]
 pub struct UpdateBuilder<'a> {
+  /// Environment information.
+  pub env: Env,
   /// Current version we are running to compare with announced version
   pub current_version: &'a str,
   /// The URLs to checks updates. We suggest at least one fallback on a different domain.
@@ -181,22 +186,17 @@ pub struct UpdateBuilder<'a> {
   pub executable_path: Option<PathBuf>,
 }
 
-impl<'a> Default for UpdateBuilder<'a> {
-  fn default() -> Self {
+// Create new updater instance and return an Update
+impl<'a> UpdateBuilder<'a> {
+  pub fn new(env: Env) -> Self {
     UpdateBuilder {
+      env,
       urls: Vec::new(),
       target: None,
       executable_path: None,
       current_version: env!("CARGO_PKG_VERSION"),
     }
   }
-}
-
-// Create new updater instance and return an Update
-impl<'a> UpdateBuilder<'a> {
-  pub fn new() -> Self {
-    UpdateBuilder::default()
-  }
 
   #[allow(dead_code)]
   pub fn url(mut self, url: String) -> Self {
@@ -267,7 +267,7 @@ impl<'a> UpdateBuilder<'a> {
     };
 
     // Get the extract_path from the provided executable_path
-    let extract_path = extract_path_from_executable(&executable_path);
+    let extract_path = extract_path_from_executable(&self.env, &executable_path);
 
     // Set SSL certs for linux if they aren't available.
     // We do not require to recheck in the download_and_install as we use
@@ -357,6 +357,7 @@ impl<'a> UpdateBuilder<'a> {
 
     // create our new updater
     Ok(Update {
+      env: self.env,
       target,
       extract_path,
       should_update,
@@ -372,12 +373,14 @@ impl<'a> UpdateBuilder<'a> {
   }
 }
 
-pub fn builder<'a>() -> UpdateBuilder<'a> {
-  UpdateBuilder::new()
+pub fn builder<'a>(env: Env) -> UpdateBuilder<'a> {
+  UpdateBuilder::new(env)
 }
 
 #[derive(Debug, Clone)]
 pub struct Update {
+  /// Environment information.
+  pub env: Env,
   /// Update description
   pub body: Option<String>,
   /// Should we update or not
@@ -418,7 +421,7 @@ impl Update {
     // be set with our APPIMAGE env variable, we don't need to do
     // anythin with it yet
     #[cfg(target_os = "linux")]
-    if env::var_os("APPIMAGE").is_none() {
+    if self.env.appimage.is_none() {
       return Err(Error::UnsupportedPlatform);
     }
 
@@ -718,7 +721,7 @@ pub fn get_updater_target() -> Option<String> {
 }
 
 /// Get the extract_path from the provided executable_path
-pub fn extract_path_from_executable(executable_path: &Path) -> PathBuf {
+pub fn extract_path_from_executable(env: &Env, executable_path: &Path) -> PathBuf {
   // Return the path of the current executable by default
   // Example C:\Program Files\My App\
   let extract_path = executable_path
@@ -748,7 +751,7 @@ pub fn extract_path_from_executable(executable_path: &Path) -> PathBuf {
   // We should use APPIMAGE exposed env variable
   // This is where our APPIMAGE should sit and should be replaced
   #[cfg(target_os = "linux")]
-  if let Some(app_image_path) = env::var_os("APPIMAGE") {
+  if let Some(app_image_path) = &env.appimage {
     return PathBuf::from(app_image_path);
   }
 
@@ -905,9 +908,10 @@ mod test {
   #[cfg(target_os = "macos")]
   #[test]
   fn test_app_name_in_path() {
-    let executable = extract_path_from_executable(Path::new(
-      "/Applications/updater-example.app/Contents/MacOS/updater-example",
-    ));
+    let executable = extract_path_from_executable(
+      &crate::Env::default(),
+      Path::new("/Applications/updater-example.app/Contents/MacOS/updater-example"),
+    );
     let app_name = macos_app_name_in_path(&executable);
     assert!(executable.ends_with("updater-example.app"));
     assert_eq!(app_name, "updater-example.app".to_string());
@@ -921,7 +925,7 @@ mod test {
       .with_body(generate_sample_raw_json())
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .current_version("0.0.0")
       .url(mockito::server_url())
       .build());
@@ -940,7 +944,7 @@ mod test {
       .with_body(generate_sample_raw_json())
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .current_version("0.0.0")
       .url(mockito::server_url())
       .build());
@@ -959,7 +963,7 @@ mod test {
       .with_body(generate_sample_raw_json())
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .current_version("0.0.0")
       .target("win64")
       .url(mockito::server_url())
@@ -985,7 +989,7 @@ mod test {
       .with_body(generate_sample_raw_json())
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .current_version("10.0.0")
       .url(mockito::server_url())
       .build());
@@ -1008,7 +1012,7 @@ mod test {
       ))
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .current_version("1.0.0")
       .url(format!(
         "{}/darwin/{{{{current_version}}}}",
@@ -1035,7 +1039,7 @@ mod test {
       ))
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .current_version("1.0.0")
       .url(format!(
         "{}/win64/{{{{current_version}}}}",
@@ -1061,7 +1065,7 @@ mod test {
       ))
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .current_version("10.0.0")
       .url(format!(
         "{}/darwin/{{{{current_version}}}}",
@@ -1083,7 +1087,7 @@ mod test {
       .with_body(generate_sample_raw_json())
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .url("http://badurl.www.tld/1".into())
       .url(mockito::server_url())
       .current_version("0.0.1")
@@ -1103,7 +1107,7 @@ mod test {
       .with_body(generate_sample_raw_json())
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .urls(&["http://badurl.www.tld/1".into(), mockito::server_url(),])
       .current_version("0.0.1")
       .build());
@@ -1122,7 +1126,7 @@ mod test {
       .with_body(generate_sample_bad_json())
       .create();
 
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .url(mockito::server_url())
       .current_version("0.0.1")
       .build());
@@ -1186,7 +1190,7 @@ mod test {
     let tmp_dir_path = tmp_dir_unwrap.path();
 
     // configure the updater
-    let check_update = block!(builder()
+    let check_update = block!(builder(Default::default())
       .url(mockito::server_url())
       // It should represent the executable path, that's why we add my_app.exe in our
       // test path -- in production you shouldn't have to provide it

+ 8 - 5
core/tauri/src/updater/mod.rs

@@ -336,7 +336,7 @@ use crate::{
   api::{dialog::ask, process::restart},
   runtime::Runtime,
   utils::config::UpdaterConfig,
-  Window,
+  Env, Manager, Window,
 };
 
 use std::sync::mpsc::channel;
@@ -381,8 +381,9 @@ pub(crate) async fn check_update_with_dialog<R: Runtime>(
   window: Window<R>,
 ) {
   if let Some(endpoints) = updater_config.endpoints.clone() {
+    let env = window.state::<Env>().inner().clone();
     // check updates
-    match self::core::builder()
+    match self::core::builder(env)
       .urls(&endpoints[..])
       .current_version(&package_info.version)
       .build()
@@ -448,8 +449,9 @@ pub(crate) fn listener<R: Runtime>(
       let window = window.clone();
       let window_isolation = window.clone();
       let pubkey = pubkey.clone();
+      let env = window.state::<Env>().inner().clone();
 
-      match self::core::builder()
+      match self::core::builder(env)
         .urls(&endpoints[..])
         .current_version(&package_info.version)
         .build()
@@ -558,13 +560,14 @@ Release Notes:
     updater.download_and_install(pubkey.clone()).await?;
 
     // Ask user if we need to restart the application
+    let env = window.state::<Env>().inner().clone();
     ask(
       Some(&window),
       "Ready to Restart",
       "The installation was successful, do you want to restart the application now?",
-      |should_exit| {
+      move |should_exit| {
         if should_exit {
-          restart();
+          restart(&env);
         }
       },
     );

+ 2 - 1
examples/resources/src-tauri/src/main.rs

@@ -9,7 +9,7 @@
 
 #[cfg(not(any(feature = "api-all", feature = "shell-all", feature = "shell-execute")))]
 fn main() {
-  eprintln!("Not supported without `api-all`, `shell-all` or `shell-execute`")
+  eprintln!("Not supported without `api-all`, `shell-all` and `shell-execute`")
 }
 
 #[cfg(any(feature = "api-all", feature = "shell-all", feature = "shell-execute"))]
@@ -25,6 +25,7 @@ fn main() {
   let script_path = resolve_path(
     context.config(),
     context.package_info(),
+    &Default::default(),
     "assets/index.js",
     Some(BaseDirectory::Resource),
   )