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

This commit is contained in:
Lucas Nogueira 2022-01-09 10:57:29 -03:00
parent 4de285c396
commit 7209fdf732
No known key found for this signature in database
GPG Key ID: 2714B66BCFB01F7F
16 changed files with 193 additions and 100 deletions

5
.changes/core-env.md Normal file
View File

@ -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.

View File

@ -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`. /// The result type of `tauri-utils`.
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@
#[cfg(not(any(feature = "api-all", feature = "shell-all", feature = "shell-execute")))] #[cfg(not(any(feature = "api-all", feature = "shell-all", feature = "shell-execute")))]
fn main() { 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"))] #[cfg(any(feature = "api-all", feature = "shell-all", feature = "shell-execute"))]
@ -25,6 +25,7 @@ fn main() {
let script_path = resolve_path( let script_path = resolve_path(
context.config(), context.config(),
context.package_info(), context.package_info(),
&Default::default(),
"assets/index.js", "assets/index.js",
Some(BaseDirectory::Resource), Some(BaseDirectory::Resource),
) )