@@ -11,7 +11,7 @@ use tokio::sync::Mutex;
1111use tauri:: { AppHandle , Emitter , Manager , Runtime } ;
1212
1313use crate :: { STATE , TAURI_APP , ChatType , Attachment } ;
14- use crate :: { util, crypto , net, db, mls} ;
14+ use crate :: { util, net, db, mls} ;
1515use crate :: util:: hex_string_to_bytes;
1616
1717/// Global set of attachment IDs currently being downloaded.
@@ -108,62 +108,36 @@ pub(crate) fn resolve_unique_filename(dir: &std::path::Path, name: &str) -> std:
108108///
109109/// Returns (path, content_hash) if successful, or an error message if unsuccessful
110110pub async fn decrypt_and_save_attachment < R : Runtime > (
111- handle : & AppHandle < R > ,
111+ _handle : & AppHandle < R > ,
112112 encrypted_data : & [ u8 ] ,
113113 attachment : & Attachment
114114) -> Result < ( std:: path:: PathBuf , String ) , String > {
115- // Decrypt the attachment using the appropriate method
116- let decrypted_data = if let Some ( ref group_id) = attachment. group_id {
117- // MLS attachment - use MDK's MIP-04 decryption
118- decrypt_mls_attachment ( encrypted_data, attachment, group_id) . await ?
119- } else {
120- // DM attachment - use explicit key/nonce with AES-GCM
121- crypto:: decrypt_data ( encrypted_data, & attachment. key , & attachment. nonce )
122- . map_err ( |e| e. to_string ( ) ) ?
123- } ;
115+ if let Some ( ref group_id) = attachment. group_id {
116+ // MLS attachment — MDK's MIP-04 decryption (stays in src-tauri)
117+ let decrypted_data = decrypt_mls_attachment ( encrypted_data, attachment, group_id) . await ?;
118+ let file_hash = util:: calculate_file_hash ( & decrypted_data) ;
124119
125- // Calculate the hash of the decrypted file
126- let file_hash = util :: calculate_file_hash ( & decrypted_data ) ;
120+ let dir = vector_core :: db :: get_download_dir ( ) ;
121+ std :: fs :: create_dir_all ( & dir ) . map_err ( |e| format ! ( "Failed to create directory: {}" , e ) ) ? ;
127122
128- // Choose the appropriate base directory based on platform
129- let base_directory = if cfg ! ( target_os = "ios" ) {
130- tauri:: path:: BaseDirectory :: Document
131- } else {
132- tauri:: path:: BaseDirectory :: Download
133- } ;
134-
135- // Resolve the directory path using the determined base directory
136- let dir = handle. path ( ) . resolve ( "vector" , base_directory) . unwrap ( ) ;
137-
138- // Use human-readable filename if available, otherwise fall back to hash-based
139- let target_name = if attachment. name . is_empty ( ) {
140- format ! ( "{}.{}" , file_hash, attachment. extension)
123+ let target_name = if attachment. name . is_empty ( ) {
124+ format ! ( "{}.{}" , file_hash, attachment. extension)
125+ } else {
126+ attachment. name . clone ( )
127+ } ;
128+
129+ let file_path = resolve_unique_filename ( & dir, & target_name) ;
130+ let tmp_path = dir. join ( format ! ( ".{}.{}.tmp" , file_hash, attachment. extension) ) ;
131+ std:: fs:: write ( & tmp_path, & decrypted_data) . map_err ( |e| format ! ( "Failed to write file: {}" , e) ) ?;
132+ std:: fs:: rename ( & tmp_path, & file_path) . map_err ( |e| format ! ( "Failed to rename file: {}" , e) ) ?;
133+ Ok ( ( file_path, file_hash) )
141134 } else {
142- attachment. name . clone ( )
143- } ;
144-
145- // Create the vector directory if it doesn't exist
146- std:: fs:: create_dir_all ( & dir) . map_err ( |e| format ! ( "Failed to create directory: {}" , e) ) ?;
147-
148- // If file with same name + same size + same hash already exists, reuse it (content dedup)
149- let candidate = dir. join ( & target_name) ;
150- let already_exists = candidate. exists ( )
151- && std:: fs:: metadata ( & candidate) . map ( |m| m. len ( ) == decrypted_data. len ( ) as u64 ) . unwrap_or ( false )
152- && std:: fs:: read ( & candidate) . map ( |b| util:: calculate_file_hash ( & b) == file_hash) . unwrap_or ( false ) ;
153-
154- if already_exists {
155- return Ok ( ( candidate, file_hash) ) ;
135+ // DM attachment — delegates to vector-core
136+ vector_core:: crypto:: decrypt_and_save_attachment (
137+ encrypted_data, & attachment. key , & attachment. nonce ,
138+ & attachment. name , & attachment. extension ,
139+ )
156140 }
157-
158- let file_path = resolve_unique_filename ( & dir, & target_name) ;
159-
160- // Atomic write: write to temp file then rename, so the file is never 0 bytes
161- // (prevents corrupted state if another thread reads concurrently on macOS APFS)
162- let tmp_path = dir. join ( format ! ( ".{}.{}.tmp" , file_hash, attachment. extension) ) ;
163- std:: fs:: write ( & tmp_path, & decrypted_data) . map_err ( |e| format ! ( "Failed to write file: {}" , e) ) ?;
164- std:: fs:: rename ( & tmp_path, & file_path) . map_err ( |e| format ! ( "Failed to rename file: {}" , e) ) ?;
165-
166- Ok ( ( file_path, file_hash) )
167141}
168142
169143/// Decrypt an MLS attachment using MDK's MIP-04 decryption
0 commit comments