feat: v0.8.0, decrypt --edit works
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
use std::{env, fs};
|
||||
use std::env::temp_dir;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::time::{Instant, SystemTime};
|
||||
|
||||
use clap::Args;
|
||||
use flate2::Compression;
|
||||
use openpgp_card::crypto_data::Cryptogram;
|
||||
use rust_util::{
|
||||
debugging, failure, iff, information, opt_result, println_ex, simple_error, success,
|
||||
util_msg, util_size, warning, XResult,
|
||||
};
|
||||
use rust_util::{debugging, failure, iff, information, opt_result, println_ex, simple_error, success, util_cmd, util_msg, util_size, util_time, warning, XResult};
|
||||
use rust_util::util_time::UnixEpochTime;
|
||||
use x509_parser::prelude::FromDer;
|
||||
use x509_parser::x509::SubjectPublicKeyInfo;
|
||||
@@ -16,7 +17,7 @@ use yubikey::piv::{AlgorithmId, decrypt_data};
|
||||
use yubikey::YubiKey;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::{consts, crypto_simple, util, util_enc_file, util_envelop, util_file, util_pgp, util_piv};
|
||||
use crate::{cmd_encrypt, consts, crypto_simple, util, util_enc_file, util_envelop, util_file, util_pgp, util_piv};
|
||||
use crate::compress::GzStreamDecoder;
|
||||
use crate::config::TinyEncryptConfig;
|
||||
use crate::consts::{
|
||||
@@ -57,6 +58,9 @@ pub struct CmdDecrypt {
|
||||
/// Digest file
|
||||
#[arg(long, short = 'D')]
|
||||
pub digest_file: bool,
|
||||
// Edit file
|
||||
#[arg(long, short = 'E')]
|
||||
pub edit_file: bool,
|
||||
/// Digest algorithm (sha1, sha256[default], sha384, sha512 ...)
|
||||
#[arg(long, short = 'A')]
|
||||
pub digest_algorithm: Option<String>,
|
||||
@@ -127,7 +131,8 @@ pub fn decrypt_single(config: &Option<TinyEncryptConfig>,
|
||||
.unwrap_or(consts::TINY_ENC_AES_GCM);
|
||||
let cryptor = Cryptor::from(encryption_algorithm)?;
|
||||
|
||||
let do_skip_file_out = cmd_decrypt.skip_decrypt_file || cmd_decrypt.direct_print || cmd_decrypt.digest_file;
|
||||
let do_skip_file_out = cmd_decrypt.skip_decrypt_file || cmd_decrypt.direct_print
|
||||
|| cmd_decrypt.digest_file || cmd_decrypt.edit_file;
|
||||
let path_out = &path_display[0..path_display.len() - TINY_ENC_FILE_EXT.len()];
|
||||
if !do_skip_file_out { util::require_file_not_exists(path_out)?; }
|
||||
|
||||
@@ -148,27 +153,73 @@ pub fn decrypt_single(config: &Option<TinyEncryptConfig>,
|
||||
|
||||
// Decrypt to output
|
||||
if cmd_decrypt.direct_print {
|
||||
if meta.file_length > 100 * 1024 {
|
||||
failure!("File too large(more than 100K) cannot direct print on console.");
|
||||
return Ok(0);
|
||||
}
|
||||
if meta.file_length > 10 * 1024 {
|
||||
warning!("File is large(more than 10K) print on console.");
|
||||
}
|
||||
|
||||
let mut output: Vec<u8> = Vec::with_capacity(10 * 1024);
|
||||
let _ = decrypt_file(
|
||||
&mut file_in, meta.file_length, &mut output, cryptor, &key_nonce, meta.compress,
|
||||
)?;
|
||||
match String::from_utf8(output) {
|
||||
Err(_) => failure!("File content is not UTF-8 encoded."),
|
||||
Ok(output) => if cmd_decrypt.split_print {
|
||||
if let Some(output) = decrypt_limited_content_to_vec(&mut file_in, &meta, cryptor, &key_nonce)? {
|
||||
if cmd_decrypt.split_print {
|
||||
print!("{}", &output)
|
||||
} else {
|
||||
println!(">>>>> BEGIN CONTENT >>>>>\n{}\n<<<<< END CONTENT <<<<<", &output)
|
||||
}
|
||||
return Ok(meta.file_length);
|
||||
}
|
||||
return Ok(meta.file_length);
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Edit file
|
||||
if cmd_decrypt.edit_file {
|
||||
let output = match decrypt_limited_content_to_vec(&mut file_in, &meta, cryptor, &key_nonce)? {
|
||||
None => return Ok(0),
|
||||
Some(output) => output,
|
||||
};
|
||||
let editor = match env::var("EDITOR") {
|
||||
Ok(editor) => editor,
|
||||
Err(_) => return simple_error!("EDITOR not found."),
|
||||
};
|
||||
|
||||
// write file to temp file
|
||||
let temp_dir = temp_dir();
|
||||
let current_millis = util_time::get_current_millis();
|
||||
let temp_file = temp_dir.join(&format!("tmp_file_{}_{}", current_millis, path_out));
|
||||
information!("Temp file: {}", temp_file.display());
|
||||
opt_result!(fs::write(&temp_file, &output), "Write temp file failed: {}");
|
||||
|
||||
let do_edit_file = || -> XResult<()> {
|
||||
// run editor
|
||||
let mut command = Command::new(&editor);
|
||||
command.arg(temp_file.to_str().expect("Get temp file path failed."));
|
||||
let run_cmd_result = util_cmd::run_command_and_wait(&mut command);
|
||||
debugging!("Run cmd result: {:?}", run_cmd_result);
|
||||
let run_cmd_exit_status = opt_result!(run_cmd_result, "Run cmd {} failed: {}", editor);
|
||||
if !run_cmd_exit_status.success() {
|
||||
return simple_error!("Run cmd {} failed: {:?}", editor, run_cmd_exit_status.code());
|
||||
}
|
||||
let temp_file_content = opt_result!(fs::read_to_string(&temp_file), "Read file failed: {}");
|
||||
if temp_file_content == output {
|
||||
information!("Temp file is not changed.");
|
||||
return Ok(());
|
||||
}
|
||||
success!("Temp file is changed, save file ...");
|
||||
|
||||
drop(file_in);
|
||||
let mut meta = meta;
|
||||
meta.file_length = temp_file_content.len() as u64;
|
||||
meta.file_last_modified = util_time::get_current_millis() as u64;
|
||||
let mut file_out = File::create(path)?;
|
||||
let _ = util_enc_file::write_tiny_encrypt_meta(&mut file_out, &meta, true)?;
|
||||
let compress_level = iff!(meta.compress, Some(Compression::default().level()), None);
|
||||
cmd_encrypt::encrypt_file(
|
||||
&mut temp_file_content.as_bytes(), meta.file_length, &mut file_out, cryptor,
|
||||
&key_nonce, &compress_level,
|
||||
)?;
|
||||
drop(file_out);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
let do_edit_file_result = do_edit_file();
|
||||
if let Err(e) = fs::remove_file(&temp_file) {
|
||||
warning!("Remove temp file: {} failed: {}", temp_file.display(), e)
|
||||
}
|
||||
do_edit_file_result?;
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Digest file
|
||||
@@ -210,6 +261,27 @@ pub fn decrypt_single(config: &Option<TinyEncryptConfig>,
|
||||
Ok(meta.file_length)
|
||||
}
|
||||
|
||||
fn decrypt_limited_content_to_vec(mut file_in: &mut File,
|
||||
meta: &TinyEncryptMeta, cryptor: Cryptor, key_nonce: &KeyNonce) -> XResult<Option<String>> {
|
||||
if meta.file_length > 100 * 1024 {
|
||||
failure!("File too large(more than 100K) cannot direct print on console.");
|
||||
return Ok(None);
|
||||
}
|
||||
if meta.file_length > 10 * 1024 {
|
||||
warning!("File is large(more than 10K) print on console.");
|
||||
}
|
||||
|
||||
let mut output: Vec<u8> = Vec::with_capacity(10 * 1024);
|
||||
let _ = decrypt_file(
|
||||
&mut file_in, meta.file_length, &mut output, cryptor, &key_nonce, meta.compress,
|
||||
)?;
|
||||
match String::from_utf8(output) {
|
||||
Err(_) => failure!("File content is not UTF-8 encoded."),
|
||||
Ok(output) => return Ok(Some(output)),
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
fn decrypt_file(file_in: &mut impl Read, file_len: u64, file_out: &mut impl Write,
|
||||
cryptor: Cryptor, key_nonce: &KeyNonce, compress: bool) -> XResult<u64> {
|
||||
let mut total_len = 0_u64;
|
||||
|
||||
Reference in New Issue
Block a user