diff --git a/crates/relicario-cli/src/commands/item_build.rs b/crates/relicario-cli/src/commands/item_build.rs index a19661f..a8e3f3f 100644 --- a/crates/relicario-cli/src/commands/item_build.rs +++ b/crates/relicario-cli/src/commands/item_build.rs @@ -3,7 +3,7 @@ //! (`commands/org.rs`). Centralizing it keeps the two surfaces from drifting. use std::collections::HashMap; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; use zeroize::Zeroizing; @@ -255,23 +255,39 @@ pub(crate) fn build_totp( }))) } +/// Read a file and encrypt it as an attachment under `key`, deriving its display +/// metadata. The plaintext is held in a `Zeroizing` buffer so it is wiped after +/// encryption. Returns the encrypted blob plus (filename, mime_type, size). +pub(crate) fn encrypt_document_file( + path: &Path, + key: &Zeroizing<[u8; 32]>, + max_bytes: u64, +) -> Result<(EncryptedAttachment, String, String, u64)> { + use relicario_core::encrypt_attachment; + let bytes = Zeroizing::new( + std::fs::read(path).with_context(|| format!("failed to read {}", path.display()))?, + ); + let enc = encrypt_attachment(&bytes, key, max_bytes)?; + let filename = path + .file_name() + .ok_or_else(|| anyhow::anyhow!("file path has no filename: {}", path.display()))? + .to_string_lossy() + .into_owned(); + let mime_type = crate::parse::guess_mime(&filename); + Ok((enc, filename, mime_type, bytes.len() as u64)) +} + pub(crate) fn build_document( title: String, file: PathBuf, key: &Zeroizing<[u8; 32]>, max_bytes: u64, ) -> Result<(Item, EncryptedAttachment)> { use relicario_core::item_types::DocumentCore; - use relicario_core::{encrypt_attachment, AttachmentRef}; - let bytes = std::fs::read(&file).with_context(|| format!("failed to read {}", file.display()))?; - let enc = encrypt_attachment(&bytes, key, max_bytes)?; - let filename = file.file_name() - .ok_or_else(|| anyhow::anyhow!("file path has no filename: {}", file.display()))? - .to_string_lossy().into_owned(); - let mime_type = crate::parse::guess_mime(&filename); - let primary_attachment = enc.id.clone(); + use relicario_core::AttachmentRef; + let (enc, filename, mime_type, size) = encrypt_document_file(&file, key, max_bytes)?; let mut item = Item::new(title, ItemCore::Document(DocumentCore { - filename: filename.clone(), mime_type: mime_type.clone(), primary_attachment: primary_attachment.clone(), + filename: filename.clone(), mime_type: mime_type.clone(), primary_attachment: enc.id.clone(), })); item.attachments.push(AttachmentRef { - id: primary_attachment, filename, mime_type, size: bytes.len() as u64, created: item.created, + id: enc.id.clone(), filename, mime_type, size, created: item.created, }); Ok((item, enc)) } diff --git a/crates/relicario-cli/src/commands/org.rs b/crates/relicario-cli/src/commands/org.rs index 0e08220..c87448c 100644 --- a/crates/relicario-cli/src/commands/org.rs +++ b/crates/relicario-cli/src/commands/org.rs @@ -1038,25 +1038,18 @@ pub fn run_edit(dir: &Path, query: &str, totp_qr: Option, fi ItemCore::Key(k) => ib::edit_key(k, history)?, ItemCore::Document(d) => { if let Some(path) = &file { - let bytes = std::fs::read(path) - .with_context(|| format!("read {}", path.display()))?; - let enc = relicario_core::encrypt_attachment( - &bytes, vault.key(), crate::org_session::DEFAULT_ORG_ATTACHMENT_MAX_BYTES)?; + let (enc, filename, mime_type, size) = ib::encrypt_document_file( + path, vault.key(), crate::org_session::DEFAULT_ORG_ATTACHMENT_MAX_BYTES)?; vault.remove_item_attachments(&collection, &id)?; let rel = vault.save_attachment(&collection, &id, &enc)?; - let filename = path - .file_name() - .ok_or_else(|| anyhow::anyhow!("file path has no filename: {}", path.display()))? - .to_string_lossy() - .into_owned(); - d.mime_type = crate::parse::guess_mime(&filename); - d.primary_attachment = enc.id.clone(); d.filename = filename.clone(); + d.mime_type = mime_type.clone(); + d.primary_attachment = enc.id.clone(); new_doc_attachments = Some(vec![relicario_core::AttachmentRef { id: enc.id, filename, - mime_type: d.mime_type.clone(), - size: bytes.len() as u64, + mime_type, + size, created: now_unix(), }]); doc_attachment_rel = Some(rel);