1use anyhow::{Context, Result};
2use base64::{Engine as _, prelude::BASE64_STANDARD};
3use serde::{Deserialize, Serialize};
4use std::{collections::HashMap, path::PathBuf};
5
6use crate::sinks::{progress::ProgressSink, result::ResultSink};
7use relay_lib::{
8 crypto::{SigningKey, StaticSecret, hex},
9 prelude::{Address, InboxId, KeyRecord},
10};
11
12#[derive(Clone, Serialize, Deserialize)]
13pub struct Identity {
14 pub pub_record: KeyRecord,
15 pub inboxes: Vec<InboxId>,
16 #[serde(with = "hex::serde")]
17 pub signing_key: [u8; 32],
18 #[serde(with = "hex::serde")]
19 pub static_secret: [u8; 32],
20}
21
22impl Identity {
23 pub fn signing_key(&self) -> SigningKey {
24 SigningKey::from_bytes(&self.signing_key)
25 }
26
27 pub fn static_secret(&self) -> StaticSecret {
28 StaticSecret::from(self.static_secret)
29 }
30
31 pub fn address(&self) -> Result<Address> {
32 Address::parse(&self.pub_record.id).context("Failed to parse address from identity")
33 }
34
35 pub fn print(&self, level: u32, result: &dyn ResultSink) {
36 result.section(level, &self.pub_record.id);
37 result.bullet_label(
38 level + 1,
39 "Created",
40 &self.pub_record.created_at.to_string(),
41 );
42
43 if let Some(expires_at) = self.pub_record.expires_at {
44 result.bullet_label(level + 1, "Expires", &expires_at.to_string());
45 } else {
46 result.bullet(level + 1, "Never Expires");
47 }
48
49 if !self.inboxes.is_empty() {
50 let mut inboxes = self.inboxes.clone();
51 inboxes.sort_by(|a, b| a.canonical().cmp(b.canonical()));
52
53 result.section(level + 1, "Inboxes");
54 for inbox in inboxes.iter() {
55 result.bullet(level + 2, &inbox.to_string());
56 }
57 }
58 }
59}
60
61#[derive(Clone, Serialize, Deserialize)]
62pub struct StorageRoot {
63 pub identities: HashMap<Address, Identity>,
64}
65
66impl StorageRoot {
67 pub fn get_identity(&self, address: &Address) -> Option<&Identity> {
68 let mut address = address.clone();
69 address.inbox = None;
70 self.identities.get(&address)
71 }
72
73 pub fn export(&self, progress: &mut dyn ProgressSink, addresses: &[Address]) -> Result<String> {
74 progress.step("Validating addresses", "Validated addresses");
75 for address in addresses.iter() {
76 let mut addr = address.clone();
77 addr.inbox = None;
78 if !self.identities.contains_key(&addr) {
79 progress.abort(&format!(
80 "No identity found for address {}",
81 address.canonical()
82 ));
83 }
84 if address.inbox().is_some() {
85 progress.abort("Please specify addresses without inbox IDs");
86 }
87 }
88
89 progress.step("Exporting identities", "Exported identities");
90 let identities = self
91 .identities
92 .iter()
93 .filter(|(addr, _)| addresses.contains(addr))
94 .map(|(_, identity)| identity.clone())
95 .collect::<Vec<_>>();
96 let json = serde_json::to_vec(&identities)
97 .unwrap_or_else(|_| progress.abort("Failed to serialize identities"));
98 Ok(BASE64_STANDARD.encode(json))
99 }
100
101 pub fn import(
102 &mut self,
103 progress: &mut dyn ProgressSink,
104 data: &str,
105 replace: bool,
106 ) -> Result<Vec<Address>> {
107 progress.step("Decoding identity data", "Decoded identity data");
108 let json = BASE64_STANDARD
109 .decode(data)
110 .unwrap_or_else(|_| progress.abort("Failed to decode base64 identity data"));
111 let identities: Vec<Identity> = serde_json::from_slice(&json)
112 .unwrap_or_else(|_| progress.abort("Failed to deserialize identity data"));
113 let identities = identities
114 .into_iter()
115 .map(|identity| {
116 let mut address = identity.address().unwrap_or_else(|_| {
117 progress.abort(&format!(
118 "Failed to parse address from identity with ID {}",
119 identity.pub_record.id
120 ));
121 });
122 address.inbox = None;
123 (address, identity)
124 })
125 .collect::<HashMap<_, _>>();
126
127 progress.step(
128 "Checking for existing identities",
129 "Checked for existing identities",
130 );
131 for (addr, _) in identities.iter() {
132 if self.identities.contains_key(addr) {
133 if replace {
134 progress.warn(&format!("Replacing existing identity for address {}", addr));
135 } else {
136 progress.abort(&format!(
137 "Identity for address {} already exists. Use --replace to overwrite.",
138 addr
139 ));
140 }
141 }
142 }
143
144 progress.step("Importing identities", "Imported identities");
145 let addresses: Vec<Address> = identities.keys().cloned().collect();
146 self.identities.extend(identities);
147
148 Ok(addresses)
149 }
150
151 pub fn delete_identity(&mut self, address: &Address) -> Result<()> {
152 let mut address = address.clone();
153 address.inbox = None;
154 if self.identities.remove(&address).is_none() {
155 anyhow::bail!("No identity found for address {}", address.canonical());
156 }
157 Ok(())
158 }
159}
160
161#[derive(Clone)]
162pub struct Storage {
163 pub root: StorageRoot,
164}
165
166impl Default for Storage {
167 fn default() -> Self {
168 Self {
169 root: StorageRoot {
170 identities: HashMap::new(),
171 },
172 }
173 }
174}
175
176impl Storage {
177 pub fn path() -> Result<PathBuf> {
178 let mut path = dirs::data_dir().context("Could not find data directory")?;
179 path.push("relay_cli");
180 std::fs::create_dir_all(&path).context("Could not create data directory")?;
181 path.push("storage.ron");
182 Ok(path)
183 }
184
185 pub fn init() -> Result<Self> {
186 if !Self::path()?.exists() {
187 Self::default().save()?;
188 }
189
190 let ron = std::fs::read_to_string(Self::path()?).context("Could not read storage file")?;
191 let root: StorageRoot =
192 ron::de::from_str(&ron).context("Could not deserialize storage file")?;
193 Ok(Self { root })
194 }
195
196 pub fn save(&self) -> Result<()> {
197 let ron = ron::ser::to_string_pretty(&self.root, ron::ser::PrettyConfig::default())
198 .context("Could not serialize storage")?;
199 std::fs::write(Self::path()?, ron).context("Could not write storage file")?;
200 Ok(())
201 }
202
203 pub fn delete(&self) -> Result<()> {
204 std::fs::remove_file(Self::path()?).context("Could not delete storage file")?;
205
206 let path = Self::path()?;
207 let parent = path.parent().context("Could not get parent directory")?;
208 std::fs::remove_dir_all(parent).context("Could not delete storage directory")?;
209
210 Ok(())
211 }
212
213 pub fn reset(&mut self) {
214 *self = Self::default();
215 }
216}