1use std::env;
2use std::ffi::OsStr;
3use std::path::Path;
4use std::process::{Command, ExitStatus};
5
6use anyhow::Result;
7use thiserror::Error;
8
9use crate::crypto::Key;
10use crate::util;
11
12pub const BIN_NAME: &str = "tomb";
14
15pub fn tomb_dig(tomb_file: &Path, mbs: u32, settings: TombSettings) -> Result<()> {
19 tomb(
20 [
21 "dig",
22 tomb_file
23 .to_str()
24 .expect("tomb path has invalid UTF-8 characters"),
25 "-s",
26 &format!("{mbs}"),
27 ],
28 settings,
29 )
30}
31
32pub fn tomb_forge(key_file: &Path, key: &Key, settings: TombSettings) -> Result<()> {
34 tomb(
35 [
36 "forge",
37 key_file
38 .to_str()
39 .expect("tomb key path has invalid UTF-8 characters"),
40 "-gr",
41 &key.fingerprint(false),
42 ],
43 settings,
44 )
45}
46
47pub fn tomb_lock(
49 tomb_file: &Path,
50 key_file: &Path,
51 key: &Key,
52 settings: TombSettings,
53) -> Result<()> {
54 tomb(
55 [
56 "lock",
57 tomb_file
58 .to_str()
59 .expect("tomb path has invalid UTF-8 characters"),
60 "-k",
61 key_file
62 .to_str()
63 .expect("tomb key path has invalid UTF-8 characters"),
64 "-gr",
65 &key.fingerprint(false),
66 ],
67 settings,
68 )
69}
70
71pub fn tomb_open(
73 tomb_file: &Path,
74 key_file: &Path,
75 store_dir: &Path,
76 key: Option<&Key>,
77 settings: TombSettings,
78) -> Result<()> {
79 let key_fp = key.map(|key| key.fingerprint(false));
81 let mut args = vec![
82 "open",
83 tomb_file
84 .to_str()
85 .expect("tomb path contains invalid UTF-8"),
86 "-k",
87 key_file
88 .to_str()
89 .expect("tomb key path contains invalid UTF-8"),
90 "-p",
91 ];
92 match &key_fp {
93 Some(fp) => args.extend(&["-gr", fp]),
94 None => args.extend(&["-g"]),
95 }
96 args.push(
97 store_dir
98 .to_str()
99 .expect("password store directory path contains invalid UTF-8"),
100 );
101
102 tomb(&args, settings)
104}
105
106pub fn tomb_close(tomb_file: &Path, settings: TombSettings) -> Result<()> {
108 tomb(
109 ["close", name(tomb_file).expect("failed to get tomb name")],
110 settings,
111 )
112}
113
114pub fn tomb_slam(settings: TombSettings) -> Result<()> {
116 tomb(["slam"], settings)
117}
118
119pub fn tomb_resize(
121 tomb_file: &Path,
122
123 key_file: &Path,
124 size_mb: u32,
125 settings: TombSettings,
126) -> Result<()> {
127 tomb(
128 [
129 "resize",
130 tomb_file
131 .to_str()
132 .expect("tomb path contains invalid UTF-8"),
133 "-k",
134 key_file
135 .to_str()
136 .expect("tomb key path contains invalid UTF-8"),
137 "-g",
138 "-s",
139 &format!("{size_mb}"),
140 ],
141 settings,
142 )
143}
144
145pub fn name(path: &Path) -> Option<&str> {
147 path.file_name()?.to_str()?.rsplitn(2, '.').last()
148}
149
150fn tomb<I, S>(args: I, settings: TombSettings) -> Result<()>
154where
155 I: IntoIterator<Item = S>,
156 S: AsRef<OsStr>,
157{
158 cmd_assert_status(cmd_tomb(args, settings).status().map_err(Err::Tomb)?)
159}
160
161fn cmd_tomb<I, S>(args: I, settings: TombSettings) -> Command
163where
164 I: IntoIterator<Item = S>,
165 S: AsRef<OsStr>,
166{
167 let mut cmd = if let Ok(bin) = env::var("PASSWORD_STORE_TOMB") {
169 Command::new(bin)
170 } else {
171 Command::new(BIN_NAME)
172 };
173
174 if util::env::is_wayland()
178 && !util::env::has_gpg_tty()
179 && let Some(tty) = util::tty::get_tty()
180 {
181 cmd.env("GPG_TTY", tty);
182 }
183
184 if settings.quiet {
186 cmd.arg("-q");
187 }
188 if settings.verbose {
189 cmd.arg("-D");
190 }
191 if settings.force {
192 cmd.arg("-f");
193 }
194 cmd.args(args);
195
196 cmd
197}
198
199fn cmd_assert_status(status: ExitStatus) -> Result<()> {
203 if !status.success() {
204 return Err(Err::Status(status).into());
205 }
206 Ok(())
207}
208
209#[derive(Copy, Clone)]
211pub struct TombSettings {
212 pub quiet: bool,
214
215 pub verbose: bool,
217
218 pub force: bool,
220}
221
222#[derive(Debug, Error)]
223pub enum Err {
224 #[error("failed to invoke tomb command")]
225 Tomb(#[source] std::io::Error),
226
227 #[error("tomb operation exited with non-zero status code: {0}")]
228 Status(std::process::ExitStatus),
229}