rpgpie_certificate_store/
lib.rs1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
13
14use std::path::{Path, PathBuf};
15
16use diesel::{query_dsl, ExpressionMethods, QueryDsl, RunQueryDsl, TextExpressionMethods};
17use openpgp_cert_d::{CertD, MergeResult};
18use rpgpie::certificate::Certificate;
19
20use crate::schema::{certs, emails, keyids, subkeys, userids};
21
22#[rustfmt::skip]
23mod schema;
24
25mod db;
26#[cfg(feature = "lookup")]
27mod lookup;
28mod model;
29mod util;
30
31const PRELOAD: bool = true;
35
36const DB_FILENAME: &str = "_rpgpie.sqlite";
37
38pub struct Store {
41 certd: CertD,
42 db_path: PathBuf,
43}
44
45impl Store {
46 pub fn new() -> Result<Self, Error> {
50 Self::with_base_dir(CertD::user_configured_store_path()?)
51 }
52
53 pub fn with_base_dir(base: impl AsRef<Path>) -> Result<Self, Error> {
57 let p: &Path = base.as_ref();
59 if !p.exists() {
60 std::fs::create_dir_all(p).ok();
61 }
62
63 let certd = CertD::with_base_dir(base)?;
64
65 let mut db_path = certd.base_dir().to_path_buf();
66 db_path.push(DB_FILENAME);
67
68 log::debug!("Store::new with db path {:?}", db_path);
69
70 let store = Self { certd, db_path };
71
72 store.init(PRELOAD)?;
77
78 Ok(store)
79 }
80
81 pub fn insert(&self, certificate: &Certificate) -> Result<(), Error> {
85 let serialized: Vec<u8> = certificate.try_into()?;
86
87 let fp = certificate.fingerprint();
88
89 match self.certd.insert(
90 &hex::encode(fp.as_bytes()),
91 serialized,
92 false,
93 |this, old| {
94 if old.is_none() {
95 Ok(MergeResult::Data(this))
96 } else {
97 log::warn!("Merging/updating is not yet implemented");
99
100 Err(openpgp_cert_d::Error::Other(
101 "Merging/updating is not yet implemented".into(),
102 ))
103 }
104 },
105 ) {
106 Ok((tag, _)) => {
107 let mut conn = self.conn()?;
108 Self::cache_update(certificate, tag, &mut conn)
109 }
110 Err(e) => Err(Error::Message(format!("Database insert failed {:?}", e))),
111 }
112 }
113
114 pub fn get_by_primary_fingerprint(
116 &self,
117 fingerprint: &str,
118 ) -> Result<Option<Certificate>, Error> {
119 let mut conn = self.conn()?;
120
121 self.get_by_primary_with_conn(fingerprint, &mut conn)
122 }
123
124 pub fn search_by_fingerprint(&self, fingerprint: &str) -> Result<Vec<Certificate>, Error> {
126 let mut res = vec![];
127
128 let mut conn = self.conn()?;
129
130 if let Ok(Some(primary)) = self.get_by_primary_with_conn(fingerprint, &mut conn) {
131 res.push(primary);
132 }
133
134 let matches = subkeys::table
135 .inner_join(certs::table)
136 .filter(subkeys::fp.eq(fingerprint.to_ascii_lowercase()))
137 .select(certs::fp)
138 .load::<String>(&mut conn)?;
139
140 matches
141 .iter()
142 .flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
143 .flatten()
144 .for_each(|c| res.push(c));
145
146 Ok(res)
147 }
148
149 pub fn search_by_key_id(&self, key_id: &str) -> Result<Vec<Certificate>, Error> {
151 let mut conn = self.conn()?;
152
153 let matches = keyids::table
154 .inner_join(certs::table)
155 .filter(keyids::keyid.eq(key_id.to_ascii_lowercase()))
156 .select(certs::fp)
157 .load::<String>(&mut conn)?;
158
159 let res = matches
160 .iter()
161 .flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
162 .flatten()
163 .collect();
164
165 Ok(res)
166 }
167
168 pub fn search_by_email(&self, email: &str) -> Result<Vec<Certificate>, Error> {
172 let mut conn = self.conn()?;
173
174 let matches = emails::table
175 .inner_join(certs::table)
176 .filter(emails::email.eq(email))
177 .select(certs::fp)
178 .load::<String>(&mut conn)?;
179
180 let res = matches
181 .iter()
182 .flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
183 .flatten()
184 .collect();
185
186 Ok(res)
187 }
188
189 pub fn search_exact_user_id(&self, user_id: &str) -> Result<Vec<Certificate>, Error> {
193 let mut conn = self.conn()?;
194
195 let matches = userids::table
196 .inner_join(certs::table)
197 .filter(userids::userid.eq(user_id))
198 .select(certs::fp)
199 .load::<String>(&mut conn)?;
200
201 let res = matches
202 .iter()
203 .flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
204 .flatten()
205 .collect();
206
207 Ok(res)
208 }
209
210 pub fn search_like_user_id(&self, like: &str) -> Result<Vec<Certificate>, Error> {
215 let mut conn = self.conn()?;
216
217 let matches = query_dsl::methods::GroupByDsl::group_by(
218 userids::table
219 .inner_join(certs::table)
220 .filter(userids::userid.like(like))
221 .select(certs::fp),
222 certs::id,
223 )
224 .load::<String>(&mut conn)?;
225
226 let res = matches
227 .iter()
228 .flat_map(|fp| self.get_by_primary_with_conn(fp, &mut conn))
229 .flatten()
230 .collect();
231
232 Ok(res)
233 }
234
235 #[cfg(feature = "lookup")]
241 pub fn poll(&self, idents: &[String]) -> Result<Vec<Certificate>, Error> {
242 let mut conn = self.conn()?;
243
244 Ok(lookup::get_keyservers(idents, &mut conn))
245 }
246}
247
248#[derive(thiserror::Error, Debug)]
250#[non_exhaustive]
251pub enum Error {
252 #[error("rpgpie error: {0}")]
253 Rpgpie(rpgpie::Error),
254
255 #[error("Database error")]
256 Database(diesel::result::Error),
257
258 #[error("cert-d error: {0}")]
259 Certd(openpgp_cert_d::Error),
260
261 #[error("Internal error: {0}")]
262 Message(String),
263}
264
265impl From<rpgpie::Error> for Error {
266 fn from(value: rpgpie::Error) -> Self {
267 Error::Rpgpie(value)
268 }
269}
270
271impl From<diesel::result::Error> for Error {
272 fn from(value: diesel::result::Error) -> Self {
273 Error::Database(value)
274 }
275}
276
277impl From<openpgp_cert_d::Error> for Error {
278 fn from(value: openpgp_cert_d::Error) -> Self {
279 Error::Certd(value)
280 }
281}