@ -15,7 +15,7 @@ use aho_corasick::AhoCorasick;
use serde ::{ Deserialize , Serialize } ;
use tauri ::{
plugin ::{ Builder , TauriPlugin } ,
AppHandle, FsScope, FsScopeEvent , Manager , Runtime ,
FsScope, FsScopeEvent , Manager , Runtime ,
} ;
use tauri_plugin_fs ::{ FsExt , ScopeEvent as FsScopeEvent } ;
@ -25,7 +25,10 @@ use std::{
path ::Path ,
} ;
// Using 2 separate files so that we don't have to think about write conflicts and not break backwards compat.
const SCOPE_STATE_FILENAME : & str = ".persisted-scope" ;
#[ cfg(feature = " protocol-asset " ) ]
const ASSET_SCOPE_STATE_FILENAME : & str = ".persisted-scope-asset" ;
// Most of these patterns are just added to try to fix broken files in the wild.
// After a while we can hopefully reduce it to something like [r"[?]", r"[*]", r"\\?\\\?\"]
@ -136,29 +139,27 @@ fn forbid_path(scope: &FsScope, path: &str) {
}
}
fn save_scopes < R : Runtime > ( app : & AppHandle < R > , app_dir : & Path , scope_state_path : & Path ) {
if let Some ( fs_scope ) = app . try_fs_scope ( ) {
let scope = Scope {
allowed_paths : fs_scope
. allowed_patterns ( )
. into_iter ( )
. map ( | p | p . to_string ( ) )
. collect ( ) ,
forbidden_patterns : fs_scope
. forbidden_patterns ( )
. into_iter ( )
. map ( | p | p . to_string ( ) )
. collect ( ) ,
} ;
let _ = create_dir_all ( app_dir )
. and_then ( | _ | File ::create ( scope_state_path ) )
. map_err ( Error ::Io )
. and_then ( | mut f | {
f . write_all ( & bincode ::serialize ( & scope ) . map_err ( Error ::from ) ? )
. map_err ( Into ::into )
} ) ;
}
fn save_scopes ( scope : & FsScope , app_dir : & Path , scope_state_path : & Path ) {
let scope = Scope {
allowed_paths : scope
. allowed_patterns ( )
. into_iter ( )
. map ( | p | p . to_string ( ) )
. collect ( ) ,
forbidden_patterns : scope
. forbidden_patterns ( )
. into_iter ( )
. map ( | p | p . to_string ( ) )
. collect ( ) ,
} ;
let _ = create_dir_all ( app_dir )
. and_then ( | _ | File ::create ( scope_state_path ) )
. map_err ( Error ::Io )
. and_then ( | mut f | {
f . write_all ( & bincode ::serialize ( & scope ) . map_err ( Error ::from ) ? )
. map_err ( Into ::into )
} ) ;
}
pub fn init < R : Runtime > ( ) -> TauriPlugin < R > {
@ -169,20 +170,21 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
let app = app . clone ( ) ;
let app_dir = app . path ( ) . app_data_dir ( ) ;
if let Ok ( app_dir ) = app_dir {
let scope_state_path = app_dir . join ( SCOPE_STATE_FILENAME ) ;
if let Some ( app_dir ) = app_dir {
let fs_scope_state_path = app_dir . join ( SCOPE_STATE_FILENAME ) ;
#[ cfg(feature = " protocol-asset " ) ]
let asset_scope_state_path = app_dir . join ( ASSET_SCOPE_STATE_FILENAME ) ;
if let Some ( s ) = fs_scope {
let _ = s . forbid_file ( & scope_state_path ) ;
}
let _ = core_scopes . forbid_file ( & scope_state_path ) ;
let _ = fs_scope . forbid_file ( & fs_scope_state_path ) ;
#[ cfg(feature = " protocol-asset " ) ]
let _ = asset_protocol_scope . forbid_file ( & asset_scope_state_path ) ;
// We're trying to fix broken .persisted-scope files seamlessly, so we'll be running this on the values read on the saved file.
// We will still save some semi-broken values because the scope events are quite spammy and we don't want to reduce runtime performance any further.
let ac = AhoCorasick ::new ( PATTERNS ) . unwrap ( /* This should be impossible to fail since we're using a small static input */ ) ;
if scope_state_path. exists ( ) {
let scope : Scope = tauri ::api ::file ::read_binary ( & scope_state_path)
if fs_ scope_state_path. exists ( ) {
let scope : Scope = tauri ::api ::file ::read_binary ( & fs_ scope_state_path)
. map_err ( Error ::from )
. and_then ( | scope | bincode ::deserialize ( & scope ) . map_err ( Into ::into ) )
. unwrap_or_default ( ) ;
@ -190,28 +192,53 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
for allowed in & scope . allowed_paths {
let allowed = fix_pattern ( & ac , allowed ) ;
allow_path ( & fs_scope , & allowed ) ;
#[ cfg(feature = " protocol-asset " ) ]
allow_path ( & asset_protocol_scope , & allowed ) ;
}
for forbidden in & scope . forbidden_patterns {
let forbidden = fix_pattern ( & ac , forbidden ) ;
forbid_path ( & fs_scope , & forbidden ) ;
#[ cfg(feature = " protocol-asset " ) ]
forbid_path ( & asset_protocol_scope , & forbidden ) ;
}
// Manually save the fixed scopes to disk once.
// This is needed to fix broken .peristed-scope files in case the app doesn't update the scope itself.
save_scopes ( & app , & app_dir , & scope_state_path) ;
save_scopes ( & fs_scope , & app_dir , & fs_ scope_state_path) ;
}
if let Some ( s ) = fs_scope {
s . listen ( move | event | {
if let FsScopeEvent ::PathAllowed ( _ ) = event {
save_scopes ( & app , & app_dir , & scope_state_path ) ;
}
} ) ;
#[ cfg(feature = " protocol-asset " ) ]
if asset_scope_state_path . exists ( ) {
let scope : Scope = tauri ::api ::file ::read_binary ( & asset_scope_state_path )
. map_err ( Error ::from )
. and_then ( | scope | bincode ::deserialize ( & scope ) . map_err ( Into ::into ) )
. unwrap_or_default ( ) ;
for allowed in & scope . allowed_paths {
let allowed = fix_pattern ( & ac , allowed ) ;
allow_path ( & asset_protocol_scope , & allowed ) ;
}
for forbidden in & scope . forbidden_patterns {
let forbidden = fix_pattern ( & ac , forbidden ) ;
forbid_path ( & asset_protocol_scope , & forbidden ) ;
}
// Manually save the fixed scopes to disk once.
save_scopes ( & asset_protocol_scope , & app_dir , & asset_scope_state_path ) ;
}
#[ cfg(feature = " protocol-asset " ) ]
let app_dir_ = app_dir . clone ( ) ;
let fs_scope_ = fs_scope . clone ( ) ;
fs_scope . listen ( move | event | {
if let FsScopeEvent ::PathAllowed ( _ ) = event {
save_scopes ( & fs_scope_ , & app_dir , & fs_scope_state_path ) ;
}
} ) ;
#[ cfg(feature = " protocol-asset " ) ]
{
let asset_protocol_scope_ = asset_protocol_scope . clone ( ) ;
asset_protocol_scope . listen ( move | event | {
if let FsScopeEvent ::PathAllowed ( _ ) = event {
save_scopes ( & asset_protocol_scope_ , & app_dir_ , & asset_scope_state_path ) ;
}
} ) ; }
}
Ok ( ( ) )
} )