pars_core/operation/
init.rs1use std::fs;
2use std::fs::OpenOptions;
3use std::io::Write;
4use std::path::Path;
5
6use anyhow::{anyhow, Result};
7use log::debug;
8use secrecy::ExposeSecret;
9use walkdir::WalkDir;
10
11use crate::constants::default_constants::FPR_FILENAME;
12use crate::pgp::PGPClient;
13use crate::util::fs_util::{
14 backup_encrypted_file, get_dir_gpg_id_content, path_attack_check, path_to_str,
15};
16
17pub struct InitConfig {
18 pub pgp_executable: String,
19 pub keys_fpr: Vec<String>,
20}
21
22fn write_new_fpr_file(path: &Path, fprs: &[impl AsRef<str>]) -> Result<()> {
23 let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(path)?;
24 let content = fprs.iter().enumerate().fold(String::new(), |mut acc, (i, line)| {
25 if i == fprs.len() - 1 {
26 acc.push_str(line.as_ref());
27 } else {
28 acc.push_str(line.as_ref());
29 acc.push('\n');
30 }
31 acc
32 });
33 write!(&mut file, "{}", content)?;
34 Ok(())
35}
36
37pub fn init(config: &InitConfig, root: &Path, target_path: Option<&str>) -> Result<()> {
38 let target = root.join(target_path.unwrap_or_default());
39 path_attack_check(root, &target)?;
40
41 let first_init = !root.join(".gpg-id").exists();
42 if first_init && target_path.is_some() {
43 return Err(anyhow!(
44 "The repository {:?} has not been initialized yet. you can not specificy a subdirectory",
45 path_to_str(root)?
46 ));
47 }
48
49 if !root.exists() {
50 println!("Creating directory '{}'", path_to_str(root)?);
51 fs::create_dir_all(root)?;
52 }
53
54 if first_init {
55 let gpg_id_path = root.join(FPR_FILENAME);
56 write_new_fpr_file(&gpg_id_path, &config.keys_fpr)?;
57 return Ok(());
58 }
59
60 if !target.exists() {
61 println!("Creating directory '{}'", path_to_str(root)?);
62 fs::create_dir_all(&target)?;
63 }
64
65 let mut old_fprs = get_dir_gpg_id_content(root, &target)?;
67 old_fprs.sort();
68 let mut new_fprs = config.keys_fpr.clone();
69 new_fprs.sort();
70
71 if old_fprs == new_fprs {
72 println!("New fingerprints are the same as the old ones, no need to update.");
73 return Ok(());
74 }
75
76 debug!("Old fpr <{:?}>, replace with <{:?}>", old_fprs, new_fprs);
77
78 let old_client = PGPClient::new(&config.pgp_executable, &old_fprs)?;
80
81 let new_client = PGPClient::new(&config.pgp_executable, &new_fprs)?;
83
84 for entry in WalkDir::new(&target) {
85 let entry = entry?;
86 if !entry.file_type().is_file() {
87 continue;
88 }
89
90 let filepath = entry.path();
91 let filename = filepath.file_name();
92
93 if let Some(filename) = filename {
94 if filename != FPR_FILENAME {
95 let content: secrecy::SecretBox<str> =
96 old_client.decrypt_stdin(root, path_to_str(filepath)?)?;
97 let backup_path = backup_encrypted_file(filepath)?;
98 new_client.encrypt(content.expose_secret(), path_to_str(filepath)?)?;
99 fs::remove_file(backup_path)?;
100 }
101 }
102 }
103
104 write_new_fpr_file(&target.join(FPR_FILENAME), &config.keys_fpr)?;
105
106 Ok(())
107}
108
109#[cfg(test)]
110mod tests {
111
112 #[test]
113 fn init_empty_repo() {
114 }
116}