ctoolbox/storage/
db.rs

1//! Public API for db operations. Dispatches automatically over IPC to the real
2//! implementations in `ipc::dispatch::db`.
3
4use crate::formats::base64::{
5    bytes_to_standard_base64, standard_base64_to_bytes,
6};
7use crate::utilities::call_ipc_sync;
8use crate::utilities::json::jq;
9use crate::workspace::ipc_old::resolve_workspace_channel;
10use crate::{bail_if_none, debug_fmt, error, json_value, log, warn};
11use anyhow::Result;
12
13/// Helper to call IPC and extract a value using jq.
14fn ipc_get<T: std::str::FromStr>(
15    method: &str,
16    args: serde_json::Value,
17    jq_path: &str,
18) -> Option<T> {
19    let channel = resolve_workspace_channel();
20    if channel.is_err() {
21        return None;
22    }
23    let channel = channel.unwrap();
24    let ipc_result = call_ipc_sync(&channel, method, args);
25    let ipc_result = match ipc_result {
26        Ok(r) => r,
27        Err(_) => return None,
28    };
29
30    let ipc_result = ipc_result.as_str();
31
32    // Parse JSON and check for "value" key
33    let parsed: serde_json::Value = serde_json::from_str(ipc_result).ok()?;
34    let value = parsed.get("value");
35    let none = parsed.get("none");
36    if value.is_none() && none.is_none() {
37        warn!("ipc_put_delete: {} returned neither value nor none", method);
38        return None;
39    }
40    if none.is_some() {
41        return None;
42    }
43
44    let value = jq(jq_path, ipc_result).ok()?;
45    let s = value.as_str();
46    T::from_str(s).ok()
47}
48
49/// Helper to call IPC for put/delete operations.
50fn ipc_put_delete(method: &str, args: serde_json::Value) -> Result<()> {
51    let channel = resolve_workspace_channel()?;
52    let ipc_result = call_ipc_sync(&channel, method, args)?;
53    let parsed: serde_json::Value = serde_json::from_str(ipc_result.as_str())
54        .ok()
55        .ok_or(anyhow::anyhow!("Failed to parse IPC response"))?;
56    let value = parsed.get("value");
57    let error = parsed.get("error");
58    if value.is_none() && error.is_none() {
59        warn!(
60            "ipc_put_delete: {} returned neither value nor error",
61            method
62        );
63        return Err(anyhow::anyhow!("IPC response missing value; {method}"));
64    }
65    if error.is_some() {
66        let error_msg = error
67            .and_then(|e| e.as_str())
68            .unwrap_or("Unknown error")
69            .to_string();
70        error!("ipc_put_delete: {} returned error: {}", method, &error_msg);
71        return Err(anyhow::anyhow!("IPC error from {method}: {error_msg}"));
72    }
73    Ok(())
74}
75
76/// Helper to call IPC for `Vec<u64>`.
77fn ipc_get_vec_u64(method: &str, args: serde_json::Value) -> Result<Vec<u64>> {
78    let channel = resolve_workspace_channel()?;
79    let ipc_result = call_ipc_sync(&channel, method, args);
80    let ipc_result = match ipc_result {
81        Ok(r) => r,
82        Err(e) => return Err(anyhow::anyhow!("Failed to call IPC sync: {e}")),
83    };
84    debug_fmt!("ipc_get_vec_u64: {} -> {}", method, ipc_result);
85    let parsed: serde_json::Value = serde_json::from_str(ipc_result.as_str())?;
86    let value = parsed.get("value");
87    let error = parsed.get("error");
88    if value.is_none() && error.is_none() {
89        warn!(
90            "ipc_get_vec_u64: {} returned neither value nor error: {:?}",
91            method, ipc_result
92        );
93        return Err(anyhow::anyhow!("IPC response missing value; {method}"));
94    }
95    if error.is_some() {
96        return Err(anyhow::anyhow!("IPC error from {method}: {ipc_result}"));
97    }
98    let value = bail_if_none!(value);
99    let arr = bail_if_none!(value.as_array());
100    let mut result = Vec::new();
101    for v in arr {
102        if let Some(n) = v.as_u64() {
103            result.push(n);
104        } else {
105            error!("ipc_get_vec_u64: {} returned non-u64 value", method);
106            return Err(anyhow::anyhow!(
107                "IPC response contained non-u64 value; {method}"
108            ));
109        }
110    }
111    Ok(result)
112}
113
114/// Get a u64 value by string key.
115pub fn get_str_u64(db: &str, key: &str) -> Option<u64> {
116    ipc_get::<u64>(
117        "get_str_u64",
118        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key }),
119        ".value",
120    )
121}
122
123pub fn get_all_u64_keys(db: &str) -> Result<Vec<u64>> {
124    ipc_get_vec_u64(
125        "get_all_u64_keys",
126        json_value!({ "db".to_string() =>  db }),
127    )
128}
129
130/// Put a u64 value by string key.
131pub fn put_str_u64(db: &str, key: &str, value: u64) -> Result<()> {
132    ipc_put_delete(
133        "put_str_u64",
134        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key, "value".to_string() =>  value }),
135    )
136}
137
138/// Delete a u64 value by string key.
139pub fn delete_str_u64(db: &str, key: &str) -> Result<()> {
140    ipc_put_delete(
141        "delete_str_u64",
142        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key }),
143    )
144}
145
146/// Get a string value by u64 key.
147pub fn get_u64_str(db: &str, key: u64) -> Option<String> {
148    ipc_get::<String>(
149        "get_u64_str",
150        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key }),
151        ".value",
152    )
153}
154
155/// Put a string value by u64 key.
156pub fn put_u64_str(db: &str, key: u64, value: &str) -> Result<()> {
157    ipc_put_delete(
158        "put_u64_str",
159        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key, "value".to_string() =>  value }),
160    )
161}
162
163/// Delete a string value by u64 key.
164pub fn delete_u64_str(db: &str, key: u64) -> Result<()> {
165    ipc_put_delete(
166        "delete_u64_str",
167        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key }),
168    )
169}
170
171/// Get a byte vector by u64 key.
172pub fn get_u64_bytes(db: &str, key: u64) -> Option<Vec<u8>> {
173    let channel = resolve_workspace_channel();
174    if channel.is_err() {
175        return None;
176    }
177    let channel = channel.ok()?;
178    let ipc_result = call_ipc_sync(
179        &channel,
180        "get_u64_bytes",
181        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key }),
182    );
183    let ipc_result = match ipc_result {
184        Ok(r) => r,
185        Err(_) => return None,
186    };
187    let parsed: serde_json::Value =
188        serde_json::from_str(ipc_result.as_str()).ok()?;
189    let value = parsed.get("value")?.as_str()?;
190    // Seems like I ought to be able to do something like this, but apparently
191    // not:
192    // let r: Option<Vec<u8>> = serde_json::from_str(value).ok();
193    let r = standard_base64_to_bytes(value.to_string()).ok();
194    log!(format!(
195        "ipc_get: get_u64_bytes {} {} -> {} bytes",
196        db,
197        key,
198        String::from_utf8_lossy(r.clone()?.as_slice()),
199    ));
200    r
201}
202
203/// Put a byte slice by u64 key.
204pub fn put_u64_bytes(db: &str, key: u64, value: &[u8]) -> Result<()> {
205    return ipc_put_delete(
206        "put_u64_bytes",
207        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key, "value".to_string() =>  value }),
208    );
209    let encoded = bytes_to_standard_base64(value);
210    ipc_put_delete(
211        "put_u64_bytes",
212        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key, "value".to_string() =>  encoded }),
213    )
214}
215
216/// Delete a byte slice by u64 key.
217pub fn delete_u64_bytes(db: &str, key: u64) -> Result<()> {
218    ipc_put_delete(
219        "delete_u64_bytes",
220        json_value!({ "db".to_string() =>  db, "key".to_string() =>  key }),
221    )
222}
223
224/// username -> `user_id`
225pub fn get_user_id(name: &str) -> Option<u64> {
226    get_str_u64("users/ids", name)
227}
228
229pub fn get_all_user_ids() -> Result<Vec<u64>> {
230    get_all_u64_keys("users/ids_rev")
231}
232
233pub fn get_user_name(user_id: u64) -> Option<String> {
234    get_u64_str("users/ids_rev", user_id)
235}
236
237pub fn put_user_id(name: &str, user_id: u64) -> Result<()> {
238    put_str_u64("users/ids", name, user_id)?;
239    put_u64_str("users/ids_rev", user_id, name)
240}
241
242pub fn delete_user_id(name: &str, user_id: u64) -> Result<()> {
243    delete_str_u64("users/ids", name)?;
244    delete_u64_str("users/ids_rev", user_id)
245}
246
247/// username -> `user_uuid`
248pub fn get_user_uuid(id: u64) -> Option<Vec<u8>> {
249    get_u64_bytes("users/uuids", id)
250}
251
252pub fn put_user_uuid(id: u64, uuid: &[u8]) -> Result<()> {
253    put_u64_bytes("users/uuids", id, uuid)
254}
255
256pub fn delete_user_uuid(id: u64) -> Result<()> {
257    delete_u64_bytes("users/uuids", id)
258}
259
260/// `user_id` -> PHC password hash (verification only)
261pub fn get_user_auth(user_id: u64) -> Option<String> {
262    get_u64_str("users/auth", user_id)
263}
264
265pub fn put_user_auth(user_id: u64, phc_hash: &str) -> Result<()> {
266    put_u64_str("users/auth", user_id, phc_hash)
267}
268
269pub fn delete_user_auth(user_id: u64) -> Result<()> {
270    delete_u64_str("users/auth", user_id)
271}
272
273/// `user_id` -> user display name (binary data)
274pub fn get_user_display_name(user_id: u64) -> Option<Vec<u8>> {
275    get_u64_bytes("users/display_names", user_id)
276}
277
278pub fn put_user_display_name(user_id: u64, display_name: &[u8]) -> Result<()> {
279    put_u64_bytes("users/display_names", user_id, display_name)
280}
281
282pub fn delete_user_display_name(user_id: u64) -> Result<()> {
283    delete_u64_bytes("users/display_names", user_id)
284}
285/// `user_id` -> user picture (binary data)
286pub fn get_user_pictures(user_id: u64) -> Option<Vec<u8>> {
287    get_u64_bytes("users/pictures", user_id)
288}
289
290pub fn put_user_picture(user_id: u64, picture_data: &[u8]) -> Result<()> {
291    put_u64_bytes("users/pictures", user_id, picture_data)
292}
293
294pub fn delete_user_picture(user_id: u64) -> Result<()> {
295    delete_u64_bytes("users/pictures", user_id)
296}
297
298/// `user_id` -> KEK params (Argon2id PHC string)
299pub fn get_user_kek_params(user_id: u64) -> Option<Vec<u8>> {
300    get_u64_bytes("users/key_encryption_key_params", user_id)
301}
302
303pub fn put_user_kek_params(user_id: u64, kek_params: &[u8]) -> Result<()> {
304    put_u64_bytes("users/key_encryption_key_params", user_id, kek_params)
305}
306
307pub fn delete_user_kek_params(user_id: u64) -> Result<()> {
308    delete_u64_bytes("users/key_encryption_key_params", user_id)
309}
310
311/// `user_id` -> wrapped data encryption key (DEK)
312pub fn get_user_wrapped_dek(user_id: u64) -> Option<Vec<u8>> {
313    get_u64_bytes("users/wrapped_dek", user_id)
314}
315
316pub fn put_user_wrapped_dek(user_id: u64, wrapped_dek: &[u8]) -> Result<()> {
317    put_u64_bytes("users/wrapped_dek", user_id, wrapped_dek)
318}
319
320pub fn delete_user_wrapped_dek(user_id: u64) -> Result<()> {
321    delete_u64_bytes("users/wrapped_dek", user_id)
322}
323
324/// `user_id` -> public key (ed25519)
325pub fn get_user_pubkey(user_id: u64) -> Option<Vec<u8>> {
326    get_u64_bytes("users/pubkeys", user_id)
327}
328
329pub fn put_user_pubkey(user_id: u64, pubkey: &[u8]) -> Result<()> {
330    put_u64_bytes("users/pubkeys", user_id, pubkey)
331}
332
333pub fn delete_user_pubkey(user_id: u64) -> Result<()> {
334    delete_u64_bytes("users/pubkeys", user_id)
335}