1use crate::util::write_at;
2use crate::util::FingerprintUserId;
3use failure;
4use failure::Fail;
5use gpgme;
6use mktemp::Temp;
7use serde_yaml;
8use std::fmt;
9use std::fs::File;
10use std::io;
11use std::path::{Path, PathBuf};
12
13#[derive(Debug, Fail)]
14#[fail(display = "The content was not encrypted for you.")]
15pub struct DecryptionError {
16 #[cause]
17 pub cause: gpgme::Error,
18}
19
20impl DecryptionError {
21 pub fn caused_by(err: gpgme::Error, alternative_text: &'static str) -> failure::Error {
22 if err.code() == gpgme::Error::NO_SECKEY.code() {
23 failure::Error::from(DecryptionError { cause: err })
24 } else {
25 err.context(alternative_text).into()
26 }
27 }
28}
29
30#[derive(Debug, Fail)]
31pub struct EncryptionError {
32 pub msg: String,
33 pub offending_recipients: Vec<String>,
34}
35
36impl fmt::Display for EncryptionError {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 write!(f, "{}", self.msg)?;
39 for info in &self.offending_recipients {
40 write!(f, "\n{}", info)?;
41 }
42 Ok(())
43 }
44}
45
46fn find_offending_keys(ctx: &mut gpgme::Context, keys: &[gpgme::Key]) -> Result<Vec<String>, failure::Error> {
47 let mut output = Vec::new();
48 let mut obuf = Vec::<u8>::new();
49 let temp = Temp::new_file()?;
50 let temp_path = temp.to_path_buf();
51 {
52 let _ibuf = write_at(&temp_path)?;
53 }
54 let mut ibuf = File::open(&temp_path)?;
55 for key in keys {
56 if let Err(err) = ctx.encrypt(Some(key), &mut ibuf, &mut obuf) {
57 output.push(format!(
58 "Could not encrypt for recipient {} with error: {}",
59 FingerprintUserId(key),
60 err
61 ));
62 }
63 }
64 Ok(output)
65}
66
67impl EncryptionError {
68 pub fn caused_by(
69 err: gpgme::Error,
70 alternative_text: String,
71 ctx: &mut gpgme::Context,
72 keys: &[gpgme::Key],
73 ) -> failure::Error {
74 failure::Error::from(EncryptionError {
75 msg: if err.code() == gpgme::Error::UNUSABLE_PUBKEY.code() {
76 "At least one recipient you try to encrypt for is untrusted. \
77 Consider (locally) signing the key with `gpg --sign-key <recipient>` \
78 or ultimately trusting them."
79 .into()
80 } else {
81 alternative_text
82 },
83 offending_recipients: match find_offending_keys(ctx, keys) {
84 Ok(v) => v,
85 Err(e) => return e,
86 },
87 })
88 }
89}
90
91#[derive(Debug, Fail)]
92pub enum VaultError {
93 ConfigurationFileExists(PathBuf),
94 Validation(failure::Error),
95 PartitionUnsupported,
96 ReadFile {
97 #[cause]
98 cause: io::Error,
99 path: PathBuf,
100 },
101 WriteFile {
102 #[cause]
103 cause: io::Error,
104 path: PathBuf,
105 },
106 Deserialization {
107 #[cause]
108 cause: serde_yaml::Error,
109 path: PathBuf,
110 },
111 Serialization {
112 #[cause]
113 cause: serde_yaml::Error,
114 path: PathBuf,
115 },
116}
117
118impl fmt::Display for VaultError {
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120 use self::VaultError::*;
121 match *self {
122 Validation(ref err) => writeln!(f, "{}", err),
123 PartitionUnsupported => f.write_str("Cannot perform this operation on a partition"),
124 ConfigurationFileExists(ref path) => writeln!(
125 f,
126 "Cannot overwrite vault configuration file as it already exists at '{}'",
127 path.display()
128 ),
129 Serialization { ref path, .. } => writeln!(
130 f,
131 "Failed to serialize vault configuration file at '{}'",
132 path.display()
133 ),
134 Deserialization { ref path, .. } => writeln!(
135 f,
136 "Failed to deserialize vault configuration file at '{}'",
137 path.display()
138 ),
139 WriteFile { ref path, .. } => {
140 writeln!(f, "Failed to write vault configuration file at '{}'", path.display())
141 }
142 ReadFile { ref path, .. } => writeln!(f, "Failed to read vault configuration file at '{}'", path.display()),
143 }
144 }
145}
146
147pub enum IOMode {
148 Read,
149 Write,
150}
151
152impl VaultError {
153 pub fn from_io_err(cause: io::Error, path: &Path, mode: &IOMode) -> Self {
154 match *mode {
155 IOMode::Write => VaultError::WriteFile {
156 cause,
157 path: path.to_owned(),
158 },
159 IOMode::Read => VaultError::ReadFile {
160 cause,
161 path: path.to_owned(),
162 },
163 }
164 }
165}
166
167pub trait FailExt: Fail {
168 fn first_cause_of<T: Fail>(&self) -> Option<&T>;
169}
170
171impl<F> FailExt for F
172where
173 F: Fail,
174{
175 fn first_cause_of<T: Fail>(&self) -> Option<&T> {
176 self.causes().filter_map(|c| c.downcast_ref::<T>()).next()
177 }
178}
179
180pub fn first_cause_of_type<T: Fail>(root: &failure::Error) -> Option<&T> {
182 root.iter_chain().filter_map(|c| c.downcast_ref::<T>()).next()
183}