1use std::os::unix::ffi::{OsStrExt as _, OsStringExt as _};
2
3pub const VERSION: u32 = {
4 const fn unwrap(res: &Result<u32, std::num::ParseIntError>) -> u32 {
5 match res {
6 Ok(t) => *t,
7 Err(_) => panic!("failed to parse cargo version"),
8 }
9 }
10
11 let major = env!("CARGO_PKG_VERSION_MAJOR");
12 let minor = env!("CARGO_PKG_VERSION_MINOR");
13 let patch = env!("CARGO_PKG_VERSION_PATCH");
14
15 unwrap(&u32::from_str_radix(major, 10)) * 1_000_000
16 + unwrap(&u32::from_str_radix(minor, 10)) * 1_000_000
17 + unwrap(&u32::from_str_radix(patch, 10)) * 1_000_000
18};
19
20#[derive(serde::Serialize, serde::Deserialize, Debug)]
21pub struct Request {
22 tty: Option<String>,
23 environment: Option<Environment>,
24 action: Action,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 session_id: Option<String>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
31 purpose: Option<String>,
32}
33
34impl Request {
35 pub fn new(environment: Environment, action: Action) -> Self {
36 Self {
37 tty: None,
38 environment: Some(environment),
39 action,
40 session_id: None,
41 purpose: None,
42 }
43 }
44
45 pub fn new_with_session(
52 environment: Environment,
53 action: Action,
54 session_id: String,
55 purpose: Option<String>,
56 ) -> Self {
57 Self {
58 tty: None,
59 environment: Some(environment),
60 action,
61 session_id: Some(session_id),
62 purpose,
63 }
64 }
65
66 pub fn into_parts(
67 self,
68 ) -> (Action, Environment, Option<String>, Option<String>) {
69 (
70 self.action,
71 self.environment.unwrap_or_else(|| Environment {
72 tty: self.tty.map(|tty| SerializableOsString(tty.into())),
73 env_vars: vec![],
74 }),
75 self.session_id,
76 self.purpose,
77 )
78 }
79}
80
81pub const ENVIRONMENT_VARIABLES: &[&str] = &[
83 "TERM",
85 "DISPLAY",
87 "XAUTHORITY",
89 "XMODIFIERS",
91 "WAYLAND_DISPLAY",
93 "XDG_SESSION_TYPE",
95 "QT_QPA_PLATFORM",
98 "GTK_IM_MODULE",
100 "DBUS_SESSION_BUS_ADDRESS",
102 "QT_IM_MODULE",
104 "PINENTRY_USER_DATA",
106 "PINENTRY_GEOM_HINT",
108];
109
110pub static ENVIRONMENT_VARIABLES_OS: std::sync::LazyLock<
111 Vec<std::ffi::OsString>,
112> = std::sync::LazyLock::new(|| {
113 ENVIRONMENT_VARIABLES
114 .iter()
115 .map(std::ffi::OsString::from)
116 .collect()
117});
118
119#[derive(Hash, PartialEq, Eq, Debug, Clone)]
120struct SerializableOsString(std::ffi::OsString);
121
122impl serde::Serialize for SerializableOsString {
123 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
124 where
125 S: serde::Serializer,
126 {
127 serializer.serialize_str(&crate::base64::encode(self.0.as_bytes()))
128 }
129}
130
131impl<'de> serde::Deserialize<'de> for SerializableOsString {
132 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133 where
134 D: serde::Deserializer<'de>,
135 {
136 struct Visitor;
137
138 impl serde::de::Visitor<'_> for Visitor {
139 type Value = SerializableOsString;
140
141 fn expecting(
142 &self,
143 formatter: &mut std::fmt::Formatter,
144 ) -> std::fmt::Result {
145 formatter.write_str("base64 encoded os string")
146 }
147
148 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
149 where
150 E: serde::de::Error,
151 {
152 Ok(SerializableOsString(std::ffi::OsString::from_vec(
153 crate::base64::decode(s).map_err(|_| {
154 E::invalid_value(serde::de::Unexpected::Str(s), &self)
155 })?,
156 )))
157 }
158 }
159
160 deserializer.deserialize_str(Visitor)
161 }
162}
163
164#[derive(serde::Serialize, serde::Deserialize, Debug, Default, Clone)]
165pub struct Environment {
166 tty: Option<SerializableOsString>,
167 env_vars: Vec<(SerializableOsString, SerializableOsString)>,
168}
169
170impl Environment {
171 pub fn new(
172 tty: Option<std::ffi::OsString>,
173 env_vars: Vec<(std::ffi::OsString, std::ffi::OsString)>,
174 ) -> Self {
175 Self {
176 tty: tty.map(SerializableOsString),
177 env_vars: env_vars
178 .into_iter()
179 .map(|(k, v)| {
180 (SerializableOsString(k), SerializableOsString(v))
181 })
182 .collect(),
183 }
184 }
185
186 pub fn tty(&self) -> Option<&std::ffi::OsStr> {
187 self.tty.as_ref().map(|tty| tty.0.as_os_str())
188 }
189
190 pub fn env_vars(
191 &self,
192 ) -> std::collections::HashMap<std::ffi::OsString, std::ffi::OsString>
193 {
194 self.env_vars
195 .iter()
196 .map(|(var, val)| (var.0.clone(), val.0.clone()))
197 .filter(|(var, _)| (*ENVIRONMENT_VARIABLES_OS).contains(var))
198 .collect()
199 }
200}
201
202#[derive(serde::Serialize, serde::Deserialize, Debug)]
203#[serde(tag = "type")]
204pub enum Action {
205 Login,
206 Register,
207 Unlock,
208 CheckLock,
209 Lock,
210 Sync,
211 Decrypt {
212 cipherstring: String,
213 entry_key: Option<String>,
214 org_id: Option<String>,
215 },
216 Encrypt {
217 plaintext: String,
218 org_id: Option<String>,
219 },
220 ClipboardStore {
221 text: String,
222 },
223 Quit,
224 Version,
225 TouchIdEnroll,
228 TouchIdDisable,
230 TouchIdStatus,
233}
234
235#[derive(serde::Serialize, serde::Deserialize, Debug)]
236#[serde(tag = "type")]
237pub enum Response {
238 Ack,
239 Error {
240 error: String,
241 },
242 Decrypt {
243 plaintext: String,
244 },
245 Encrypt {
246 cipherstring: String,
247 },
248 Version {
249 version: u32,
250 },
251 TouchIdStatus {
252 enrolled: bool,
253 gate: String,
254 keychain_label: Option<String>,
255 },
256}