feat: v0.8.0, decrypt --edit works

This commit is contained in:
2023-12-01 00:23:20 +08:00
parent 4b426f7b13
commit 4966ddf72b
4 changed files with 217 additions and 79 deletions

View File

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