use std::env;
use std::ffi::OsStr;
use std::path::Path;
use std::process::{Command, ExitStatus};
use anyhow::Result;
use thiserror::Error;
use crate::crypto::Key;
use crate::util;
pub const BIN_NAME: &str = "tomb";
pub fn tomb_dig(tomb_file: &Path, mbs: u32, settings: TombSettings) -> Result<()> {
tomb(
[
"dig",
tomb_file
.to_str()
.expect("tomb path has invalid UTF-8 characters"),
"-s",
&format!("{mbs}"),
],
settings,
)
}
pub fn tomb_forge(key_file: &Path, key: &Key, settings: TombSettings) -> Result<()> {
tomb(
[
"forge",
key_file
.to_str()
.expect("tomb key path has invalid UTF-8 characters"),
"-gr",
&key.fingerprint(false),
],
settings,
)
}
pub fn tomb_lock(
tomb_file: &Path,
key_file: &Path,
key: &Key,
settings: TombSettings,
) -> Result<()> {
tomb(
[
"lock",
tomb_file
.to_str()
.expect("tomb path has invalid UTF-8 characters"),
"-k",
key_file
.to_str()
.expect("tomb key path has invalid UTF-8 characters"),
"-gr",
&key.fingerprint(false),
],
settings,
)
}
pub fn tomb_open(
tomb_file: &Path,
key_file: &Path,
store_dir: &Path,
key: Option<&Key>,
settings: TombSettings,
) -> Result<()> {
let key_fp = key.map(|key| key.fingerprint(false));
let mut args = vec![
"open",
tomb_file
.to_str()
.expect("tomb path contains invalid UTF-8"),
"-k",
key_file
.to_str()
.expect("tomb key path contains invalid UTF-8"),
"-p",
];
match &key_fp {
Some(fp) => args.extend(&["-gr", fp]),
None => args.extend(&["-g"]),
}
args.push(
store_dir
.to_str()
.expect("password store directory path contains invalid UTF-8"),
);
tomb(&args, settings)
}
pub fn tomb_close(tomb_file: &Path, settings: TombSettings) -> Result<()> {
tomb(
["close", name(tomb_file).expect("failed to get tomb name")],
settings,
)
}
pub fn tomb_slam(settings: TombSettings) -> Result<()> {
tomb(["slam"], settings)
}
pub fn tomb_resize(
tomb_file: &Path,
key_file: &Path,
size_mb: u32,
settings: TombSettings,
) -> Result<()> {
tomb(
[
"resize",
tomb_file
.to_str()
.expect("tomb path contains invalid UTF-8"),
"-k",
key_file
.to_str()
.expect("tomb key path contains invalid UTF-8"),
"-g",
"-s",
&format!("{size_mb}"),
],
settings,
)
}
pub fn name(path: &Path) -> Option<&str> {
path.file_name()?.to_str()?.rsplitn(2, '.').last()
}
fn tomb<I, S>(args: I, settings: TombSettings) -> Result<()>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
cmd_assert_status(cmd_tomb(args, settings).status().map_err(Err::Tomb)?)
}
fn cmd_tomb<I, S>(args: I, settings: TombSettings) -> Command
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut cmd = if let Ok(bin) = env::var("PASSWORD_STORE_TOMB") {
Command::new(bin)
} else {
Command::new(BIN_NAME)
};
if util::env::is_wayland()
&& !util::env::has_gpg_tty()
&& let Some(tty) = util::tty::get_tty()
{
cmd.env("GPG_TTY", tty);
}
if settings.quiet {
cmd.arg("-q");
}
if settings.verbose {
cmd.arg("-D");
}
if settings.force {
cmd.arg("-f");
}
cmd.args(args);
cmd
}
fn cmd_assert_status(status: ExitStatus) -> Result<()> {
if !status.success() {
return Err(Err::Status(status).into());
}
Ok(())
}
#[derive(Copy, Clone)]
pub struct TombSettings {
pub quiet: bool,
pub verbose: bool,
pub force: bool,
}
#[derive(Debug, Error)]
pub enum Err {
#[error("failed to invoke tomb command")]
Tomb(#[source] std::io::Error),
#[error("tomb operation exited with non-zero status code: {0}")]
Status(std::process::ExitStatus),
}