1use std::env;
2use std::fs;
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7use crate::error::Result;
8
9fn home_dir() -> PathBuf {
10 if let Some(home) = env::var_os("HOME") {
11 PathBuf::from(home)
12 } else if let Some(profile) = env::var_os("USERPROFILE") {
13 PathBuf::from(profile)
14 } else {
15 env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
16 }
17}
18
19pub fn config_dir() -> PathBuf {
20 home_dir().join(".config").join("ploit_malper")
21}
22
23pub fn config_file() -> PathBuf {
24 config_dir().join("config.json")
25}
26
27pub fn nvd_cache_file() -> PathBuf {
28 config_dir().join("nvd_cache.json")
29}
30
31pub fn db_schema_file() -> PathBuf {
32 config_dir().join("schema.pbsl")
33}
34
35pub fn default_sqlite_db() -> PathBuf {
36 config_dir().join("ploit_malper.db")
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(default)]
41pub struct MSFRPCConfig {
42 pub host: String,
43 pub port: u16,
44 pub username: String,
45 pub password: String,
46 pub ssl: bool,
47 pub workspace: String,
48}
49
50impl Default for MSFRPCConfig {
51 fn default() -> Self {
52 Self {
53 host: "127.0.0.1".to_string(),
54 port: 55553,
55 username: "msf".to_string(),
56 password: String::new(),
57 ssl: true,
58 workspace: "default".to_string(),
59 }
60 }
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, Default)]
64#[serde(default)]
65pub struct NVDConfig {
66 pub api_key: String,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70#[serde(default)]
71pub struct DatabaseConfig {
72 pub backend: String,
74 pub pocketbase_url: String,
76 pub pocketbase_token: String,
79 pub pocketbase_admin_email: String,
81 pub pocketbase_admin_password: String,
83 pub sqlite_path: String,
85 pub configured: bool,
87}
88
89impl Default for DatabaseConfig {
90 fn default() -> Self {
91 Self {
92 backend: "pocketbase".to_string(),
93 pocketbase_url: "http://127.0.0.1:8090".to_string(),
94 pocketbase_token: String::new(),
95 pocketbase_admin_email: String::new(),
96 pocketbase_admin_password: String::new(),
97 sqlite_path: default_sqlite_db().to_string_lossy().to_string(),
98 configured: false,
99 }
100 }
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(default)]
105pub struct AppConfig {
106 pub msfrpc: MSFRPCConfig,
107 pub nvd: NVDConfig,
108 pub database: DatabaseConfig,
109 pub report_dir: String,
110 pub last_scan_file: Option<String>,
111}
112
113impl Default for AppConfig {
114 fn default() -> Self {
115 Self {
116 msfrpc: MSFRPCConfig::default(),
117 nvd: NVDConfig::default(),
118 database: DatabaseConfig::default(),
119 report_dir: home_dir()
120 .join("ploit_malper_reports")
121 .to_string_lossy()
122 .to_string(),
123 last_scan_file: None,
124 }
125 }
126}
127
128#[derive(Debug, Clone)]
129pub struct ConfigManager {
130 pub config: AppConfig,
131 loaded: bool,
132}
133
134impl ConfigManager {
135 pub fn new() -> Self {
136 Self {
137 config: AppConfig::default(),
138 loaded: false,
139 }
140 }
141
142 pub fn load(&mut self) -> bool {
143 let path = config_file();
144 if !path.exists() {
145 return false;
146 }
147
148 match fs::read_to_string(&path) {
149 Ok(raw) => match serde_json::from_str::<AppConfig>(&raw) {
150 Ok(config) => {
151 self.config = config;
152 self.loaded = true;
153 true
154 }
155 Err(_) => false,
156 },
157 Err(_) => false,
158 }
159 }
160
161 pub fn save(&mut self) -> Result<()> {
162 let dir = config_dir();
163 fs::create_dir_all(&dir)?;
164 let path = config_file();
165 let data = serde_json::to_string_pretty(&self.config)?;
166 fs::write(path, data)?;
167 self.loaded = true;
168 Ok(())
169 }
170
171 pub fn reset(&mut self) -> Result<()> {
172 let config_path = config_file();
173 if config_path.exists() {
174 fs::remove_file(config_path)?;
175 }
176
177 let cache_path = nvd_cache_file();
178 if cache_path.exists() {
179 fs::remove_file(cache_path)?;
180 }
181
182 self.config = AppConfig::default();
183 self.loaded = false;
184 Ok(())
185 }
186
187 pub fn is_configured(&self) -> bool {
188 self.loaded && !self.config.msfrpc.password.is_empty()
189 }
190
191 pub fn is_nvd_configured(&self) -> bool {
192 !self.config.nvd.api_key.is_empty()
193 }
194
195 pub fn get_msfrpc_config(&self) -> &MSFRPCConfig {
196 &self.config.msfrpc
197 }
198
199 pub fn get_nvd_config(&self) -> &NVDConfig {
200 &self.config.nvd
201 }
202
203 pub fn get_database_config(&self) -> &DatabaseConfig {
204 &self.config.database
205 }
206
207 pub fn database_configured(&self) -> bool {
208 self.config.database.configured
209 }
210}
211
212impl Default for ConfigManager {
213 fn default() -> Self {
214 Self::new()
215 }
216}