fur_cli/security/
unlock.rs1use std::fs;
2use std::path::Path;
3
4use clap::Parser;
5use rpassword::read_password;
6
7use crate::security::{crypto, io, state};
8
9#[derive(Parser, Clone)]
10pub struct UnlockArgs {
11 #[arg(long)]
12 pub hide: bool,
13}
14
15pub fn run_unlock(args: UnlockArgs) {
16 if !state::is_locked() {
17 println!("🔓 Project already unlocked.");
18 return;
19 }
20
21 let pass = if args.hide {
22 println!("🔑 Enter password:");
23 read_password().unwrap()
24 } else {
25 io::read_visible_password()
26 };
27
28 if !verify_password(&pass) {
29 println!("❌ Incorrect password. Project remains locked.");
30 return;
31 }
32
33 decrypt_project(&pass);
34
35 state::remove_lock();
36
37 println!("🔓 Project decrypted.");
38}
39
40fn verify_password(password: &str) -> bool {
41 let check_path = Path::new(".fur/.lockcheck");
42
43 let bytes = match std::fs::read(check_path) {
44 Ok(b) => b,
45 Err(_) => return false,
46 };
47
48 let decrypted = match crypto::decrypt(&bytes, password) {
49 Ok(d) => d,
50 Err(_) => return false,
51 };
52
53 match std::str::from_utf8(&decrypted) {
54 Ok(text) => text.trim() == "FUR_LOCK_CHECK_V1",
55 Err(_) => false,
56 }
57}
58
59fn decrypt_project(password: &str) {
60 decrypt_dir(".fur/messages", password);
61 decrypt_dir(".fur/threads", password);
62
63 decrypt_file(".fur/index.json", password);
64 decrypt_file(".fur/.lockcheck", password);
65
66 decrypt_markdowns(password);
67}
68
69fn decrypt_dir(dir: &str, password: &str) {
70 let path = Path::new(dir);
71
72 if let Ok(entries) = fs::read_dir(path) {
73 for e in entries.flatten() {
74 let p = e.path();
75
76 if p.is_file() {
77 io::decrypt_file(&p, password);
78 }
79 }
80 }
81}
82
83fn decrypt_file(path: &str, password: &str) {
84 let p = Path::new(path);
85
86 if p.exists() {
87 io::decrypt_file(p, password);
88 }
89}
90
91fn decrypt_markdowns(password: &str) {
92 let chats = Path::new("chats");
93
94 if !chats.exists() {
95 return;
96 }
97
98 if let Ok(entries) = fs::read_dir(chats) {
99 for e in entries.flatten() {
100 let p = e.path();
101
102 if p.is_file() {
103 io::decrypt_file(&p, password);
104 }
105 }
106 }
107}