pars_core/operation/
init.rs

1use 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    // Try to read old fingerprints from the directory
66    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    // Create old client using old fingerprints
79    let old_client = PGPClient::new(&config.pgp_executable, &old_fprs)?;
80
81    // Create new client using new fingerprints
82    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        // unimplemented!("fuck me")
115    }
116}