Skip to main content

sequoia_cert_store/
store.rs

1use std::borrow::Cow;
2use std::str;
3use std::sync::Arc;
4use std::sync::atomic::AtomicUsize;
5use std::sync::atomic::Ordering;
6
7use anyhow::Context;
8
9use sequoia_openpgp as openpgp;
10use openpgp::Fingerprint;
11use openpgp::KeyHandle;
12use openpgp::Result;
13use openpgp::cert::Cert;
14use openpgp::cert::ValidCert;
15use openpgp::packet::UserID;
16
17#[cfg(certd)]
18pub use openpgp_cert_d;
19
20mod userid_index;
21pub use userid_index::UserIDIndex;
22
23#[cfg(certd)]
24pub mod certd;
25#[cfg(certd)]
26pub use certd::CertD;
27
28pub mod certs;
29pub use certs::Certs;
30
31// The keyserver backend is optional.
32#[cfg(feature = "keyserver")]
33pub mod keyserver;
34#[cfg(feature = "keyserver")]
35pub use keyserver::KeyServer;
36
37// If it is disabled, we swap in a dummy to reduce changes to the rest
38// of the code.
39pub mod no_keyserver;
40#[cfg(not(feature = "keyserver"))]
41use no_keyserver as keyserver;
42#[cfg(not(feature = "keyserver"))]
43pub(crate) use keyserver::KeyServer;
44
45#[cfg(pep)]
46pub mod pep;
47#[cfg(pep)]
48pub use pep::Pep;
49
50use super::TRACE;
51
52use crate::LazyCert;
53
54#[derive(Debug, Clone)]
55pub struct UserIDQueryParams {
56    anchor_start: bool,
57    anchor_end: bool,
58    email: bool,
59    ignore_case: bool,
60}
61assert_send_and_sync!(UserIDQueryParams);
62
63impl UserIDQueryParams {
64    /// Returns a new `UserIDQueryParams`.
65    ///
66    /// By default, the query is configured to perform an exact match
67    /// on the User ID.  That is, the pattern must match the start and
68    /// end of the User ID, and case is considered significant.
69    pub fn new() -> Self {
70        Self {
71            anchor_start: true,
72            anchor_end: true,
73            email: false,
74            ignore_case: false,
75        }
76    }
77
78    /// Sets whether the pattern must match the start of the User ID
79    /// or email address.
80    pub fn set_anchor_start(&mut self, anchor_start: bool) -> &mut Self {
81        self.anchor_start = anchor_start;
82        self
83    }
84
85    /// Returns whether the pattern must match the start of the User
86    /// ID or email address.
87    pub fn anchor_start(&self) -> bool {
88        self.anchor_start
89    }
90
91    /// Sets whether the pattern must match the end of the User
92    /// ID or email address.
93    pub fn set_anchor_end(&mut self, anchor_end: bool) -> &mut Self {
94        self.anchor_end = anchor_end;
95        self
96    }
97
98    /// Returns whether the pattern must match the end of the User
99    /// ID or email address.
100    pub fn anchor_end(&self) -> bool {
101        self.anchor_end
102    }
103
104    /// Sets whether the pattern must match the User ID or the
105    /// normalized email address.
106    ///
107    /// The email address to check the pattern against is extracted
108    /// from the User ID using [`UserID::email_normalized`].
109    ///
110    /// Note: the pattern is *not* normalized, even if the anchors are
111    /// set.  If you want to search by email address you need to
112    /// normalize it your yourself.
113    ///
114    /// [`UserID::email_normalized`]: https://docs.rs/sequoia-openpgp/latest/sequoia_openpgp/packet/prelude/struct.UserID.html#method.email_normalized
115    pub fn set_email(&mut self, email: bool) -> &mut Self {
116        self.email = email;
117        self
118    }
119
120    /// Returns whether the pattern must match the User ID or the
121    /// normalized email address.
122    ///
123    /// See [`UserIDQueryParams::set_email`] for more details.
124    pub fn email(&self) -> bool {
125        self.email
126    }
127
128    /// Sets whether to ignore the case when matching the User ID or
129    /// email address.
130    ///
131    /// Uses the empty local.
132    ///
133    /// When matching an email address, the domain is always matched
134    /// in a case insensitive manner.  The localpart, however, is
135    /// matched in a case sensitive manner by default.
136    pub fn set_ignore_case(&mut self, ignore_case: bool) -> &mut Self {
137        self.ignore_case = ignore_case;
138        self
139    }
140
141    /// Returns whether to ignore the case when matching the User ID
142    /// or email address.
143    ///
144    /// See [`UserIDQueryParams::set_ignore_case`] for more details.
145    pub fn ignore_case(&self) -> bool {
146        self.ignore_case
147    }
148
149    /// Checks that the User ID satisfies the constraints.
150    pub fn check(&self, userid: &UserID, pattern: &str) -> bool {
151        tracer!(TRACE, "UserIDQueryParams::check");
152
153        // XXX: If you change this function,
154        // UserIDIndex::select_userid contains similar code.  Update
155        // that too.
156        match self {
157            UserIDQueryParams {
158                anchor_start: true,
159                anchor_end: true,
160                email: false,
161                ignore_case: false,
162            } => {
163                // Exact User ID match.
164                userid.value() == pattern.as_bytes()
165            }
166
167            UserIDQueryParams {
168                anchor_start: true,
169                anchor_end: true,
170                email: true,
171                ignore_case: false,
172            } => {
173                // Exact email match.
174                if let Ok(Some(email)) = userid.email_normalized() {
175                    email == pattern
176                } else {
177                    false
178                }
179            }
180
181            UserIDQueryParams {
182                anchor_start,
183                anchor_end,
184                email,
185                ignore_case,
186            } => {
187                t!("Considering if {:?} matches {:?} \
188                    (anchors: {}, {}, ignore case: {})",
189                   pattern, userid, anchor_start, anchor_end, ignore_case);
190
191                // Substring search.
192                let mut userid = if *email {
193                    if let Ok(Some(email)) = userid.email_normalized() {
194                        Cow::Owned(email)
195                    } else {
196                        t!("User ID does not contain a valid email address");
197                        return false;
198                    }
199                } else {
200                    String::from_utf8_lossy(userid.value())
201                };
202
203                if *ignore_case {
204                    userid = Cow::Owned(userid.to_lowercase());
205                }
206
207                let mut pattern = pattern;
208                let _pattern;
209                if *ignore_case {
210                    // Convert to lowercase without tailoring,
211                    // i.e. without taking any locale into account.
212                    // See:
213                    //
214                    //  - https://www.w3.org/International/wiki/Case_folding
215                    //  - https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase
216                    //  - http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G33992
217                    _pattern = pattern.to_lowercase();
218                    pattern = &_pattern[..];
219                }
220
221                if match (*anchor_start, *anchor_end) {
222                    (true, true) => userid == pattern,
223                    (true, false) => userid.starts_with(pattern),
224                    (false, true) => userid.ends_with(pattern),
225                    (false, false) => userid.contains(pattern),
226                }
227                {
228                    t!("*** {:?} matches {:?} (anchors: {}, {})",
229                       pattern, userid, *anchor_start, anchor_end);
230                    true
231                } else {
232                    false
233                }
234            }
235        }
236    }
237
238    /// Checks that at least one User ID satisfies the constraints.
239    pub fn check_lazy_cert(&self, cert: &LazyCert, pattern: &str) -> bool {
240        cert.userids().any(|userid| self.check(&userid, pattern))
241    }
242
243    /// Checks that at least one User ID satisfies the constraints.
244    pub fn check_cert(&self, cert: &Cert, pattern: &str) -> bool {
245        cert.userids().any(|ua| self.check(ua.userid(), pattern))
246    }
247
248    /// Checks that at least one User ID satisfies the constraints.
249    pub fn check_valid_cert(&self, vc: &ValidCert, pattern: &str) -> bool {
250        vc.userids().any(|ua| self.check(ua.userid(), pattern))
251    }
252
253    /// Returns whether the supplied email address is actually a valid
254    /// email address.
255    ///
256    /// If it is valid, returns the normalized email address.
257    pub fn is_email(email: &str) -> Result<String> {
258        let email_check = UserID::from(format!("<{}>", email));
259        match email_check.email() {
260            Ok(Some(email_check)) => {
261                if email != email_check {
262                    return Err(StoreError::InvalidEmail(
263                        email.to_string(), None).into());
264                }
265            }
266            Ok(None) => {
267                return Err(StoreError::InvalidEmail(
268                    email.to_string(), None).into());
269            }
270            Err(err) => {
271                return Err(StoreError::InvalidEmail(
272                    email.to_string(), Some(err)).into());
273            }
274        }
275
276        match UserID::from(&email[..]).email_normalized() {
277            Err(err) => {
278                Err(StoreError::InvalidEmail(
279                    email.to_string(), Some(err)).into())
280            }
281            Ok(None) => {
282                Err(StoreError::InvalidEmail(
283                    email.to_string(), None).into())
284            }
285            Ok(Some(email)) => {
286                Ok(email)
287            }
288        }
289    }
290
291    /// Returns whether the supplied domain address is actually a
292    /// valid domain for an email address.
293    ///
294    /// Returns the normalized domain.
295    pub fn is_domain(domain: &str) -> Result<String> {
296        let localpart = "user@";
297        let email = format!("{}{}", localpart, domain);
298        let email = Self::is_email(&email)?;
299
300        // We get the normalized email address back.  Chop off the
301        // username and the @.
302        assert!(email.starts_with(localpart));
303        Ok(email[localpart.len()..].to_string())
304    }
305}
306
307/// [`Store`] specific error codes.
308#[non_exhaustive]
309#[derive(thiserror::Error, Debug)]
310pub enum StoreError {
311    /// No certificate was found.
312    #[error("{0} was not found")]
313    NotFound(KeyHandle),
314
315    /// No certificate matches the search criteria.
316    #[error("No certificates matched {0}")]
317    NoMatches(String),
318
319    /// The email address does not appear to be a valid email address.
320    #[error("{0:?} does not appear to be a valid email address")]
321    InvalidEmail(String, #[source] Option<anyhow::Error>),
322}
323
324/// Status messages.
325///
326/// Status messages sent by [`StatusListener::update`].
327///
328/// The transaction id allows messages to be grouped together.  For
329/// instance, `LookupStarted` will return a new transaction id and
330/// further messages related to that lookup including `LookupFinished`
331/// and `LookupFailed` will use the same transaction id.
332#[non_exhaustive]
333#[derive(Debug)]
334pub enum StatusUpdate<'a, 'c> {
335    /// Sent when a lookup is starting.
336    ///
337    /// usize is the transaction id.
338    ///
339    /// The first `&str` is a short human-readable description of the
340    /// backend.
341    ///
342    /// `KeyHandle` is what is being looked up.
343    ///
344    /// The last `&str` is a short human-readable message describing
345    /// the look up.
346    LookupStarted(usize, &'a str, &'a KeyHandle, Option<&'a str>),
347
348    /// Sent while a lookup is on-going.
349    ///
350    /// usize is the transaction id.  It will match the transaction id
351    /// sent in the `LookupStart` message.
352    ///
353    /// The first `&str` is a short human-readable description of the
354    /// backend.
355    ///
356    /// `KeyHandle` is what was being looked up.
357    ///
358    /// The last `&str` is a short human-readable message describing
359    /// something that happened, e.g., "WKD returned FPR", etc.
360    LookupStatus(usize, &'a str, &'a KeyHandle, &'a str),
361
362    /// Sent when a lookup has been successful.
363    ///
364    /// usize is the transaction id.  It will match the transaction id
365    /// sent in the `LookupStart` message.
366    ///
367    /// The first `&str` is a short human-readable description of the
368    /// backend.
369    ///
370    /// `KeyHandle` is what was being looked up.
371    ///
372    /// The certificates are the returned results.
373    ///
374    /// The last `&str` is a short human-readable message describing
375    /// what happened, e.g., "found in cache", etc.
376    LookupFinished(usize, &'a str, &'a KeyHandle,
377                   &'a [Arc<LazyCert<'c>>], Option<&'a str>),
378
379    /// Sent when a lookup has failed.
380    ///
381    /// usize is the transaction id.  It will match the transaction id
382    /// sent in the `LookupStart` message.
383    ///
384    /// The first `&str` is a short human-readable description of the
385    /// backend.
386    ///
387    /// `KeyHandle` is what was being looked up.
388    ///
389    /// The error is the reason that the lookup failed.  A backend
390    /// should set this to `None` if no certificate was present.
391    LookupFailed(usize, &'a str, &'a KeyHandle, Option<&'a anyhow::Error>),
392
393    /// Sent when a search is started.
394    ///
395    /// usize is the transaction id.
396    ///
397    /// The first `&str` is a short human-readable description of the
398    /// backend.
399    ///
400    /// The second `&str` is the pattern being searched for.
401    ///
402    /// The last `&str` is a short human-readable message describing
403    /// what is being looked up.
404    SearchStarted(usize, &'a str, &'a str, Option<&'a str>),
405
406    /// Sent while a search is on-going.
407    ///
408    /// usize is the transaction id.  It will match the transaction id
409    /// sent in the `LookupStart` message.
410    ///
411    /// The first `&str` is a short human-readable description of the
412    /// backend.
413    ///
414    /// The second `&str` is the pattern being searched for.
415    ///
416    /// The last `&str` is a short human-readable message describing
417    /// something that happened, e.g., "WKD returned FPR", etc.
418    SearchStatus(usize, &'a str, &'a str, &'a str),
419
420    /// Sent when a search is successful.
421    ///
422    /// usize is the transaction id.  It will match the transaction id
423    /// sent in the `LookupStart` message.
424    ///
425    /// The first `&str` is a short human-readable description of the
426    /// backend.
427    ///
428    /// The second `&str` is the pattern being searched for.
429    ///
430    /// The certificates are the returned results.
431    ///
432    /// The last `&str` is a short human-readable message describing
433    /// what happened, e.g., "found in cache", "found 5 matching
434    /// certificates", etc.
435    SearchFinished(usize, &'a str, &'a str, &'a [Arc<LazyCert<'c>>],
436                   Option<&'a str>),
437
438    /// Sent whenever something has been lookup successfully.
439    ///
440    /// usize is the transaction id.  It will match the transaction id
441    /// sent in the `LookupStart` message.
442    ///
443    /// The first `&str` is a short human-readable description of the
444    /// backend.
445    ///
446    /// The second `&str` is the pattern being searched for.
447    ///
448    /// The error is the reason that the search failed.  A backend
449    /// should set this to `None` if no matching certificate was
450    /// found.
451    SearchFailed(usize, &'a str, &'a str, Option<&'a anyhow::Error>),
452}
453
454impl<'a, 'c> std::fmt::Display for StatusUpdate<'a, 'c> {
455    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>)
456        -> std::result::Result<(), std::fmt::Error>
457    {
458        use StatusUpdate::*;
459
460        match self {
461            LookupStarted(_tx, backend, kh, msg) => {
462                if let Some(msg) = msg {
463                    write!(fmt, "{}: Looking up {}: {}...",
464                           backend, kh, msg)
465                } else {
466                    write!(fmt, "{}: Looking up {}...",
467                           backend, kh)
468                }
469            }
470            LookupStatus(_tx, backend, kh, msg) => {
471                write!(fmt, "{}: Looking up {}: {}",
472                       backend, kh, msg)
473            }
474            LookupFinished(_tx, backend, kh, results, msg) => {
475                if let Some(msg) = msg {
476                    write!(fmt, "{}: Looking up {}, returned {} results: {}",
477                           backend, kh, results.len(), msg)
478                } else {
479                    write!(fmt, "{}: Looking up {}, returned {} results",
480                           backend, kh, results.len())
481                }
482            }
483            LookupFailed(_tx, backend, kh, err) => {
484                if let Some(err) = err {
485                    write!(fmt, "{}: Looking up {}, failed: {}",
486                           backend, kh, err)
487                } else {
488                    write!(fmt, "{}: Looking up {}, returned no results",
489                           backend, kh)
490                }
491            }
492
493            SearchStarted(_tx, backend, pattern, msg) => {
494                if let Some(msg) = msg {
495                    write!(fmt, "{}: Searching for {:?}: {}...",
496                           backend, pattern, msg)
497                } else {
498                    write!(fmt, "{}: Searching for {:?}...",
499                           backend, pattern)
500                }
501            }
502            SearchStatus(_tx, backend, pattern, msg) => {
503                write!(fmt, "{}: Searching for {:?}: {}",
504                       backend, pattern, msg)
505            }
506            SearchFinished(_tx, backend, pattern, results, msg) => {
507                if let Some(msg) = msg {
508                    write!(fmt, "{}: Searching for {:?}, returned {} results: {}",
509                           backend, pattern, results.len(), msg)
510                } else {
511                    write!(fmt, "{}: Searching for {:?}, returned {} results",
512                           backend, pattern, results.len())
513                }
514            }
515            SearchFailed(_tx, backend, pattern, err) => {
516                if let Some(err) = err {
517                    write!(fmt, "{}: Searching for {:?} failed: {}",
518                           backend, pattern, err)
519                } else {
520                    write!(fmt, "{}: Searching for {:?}, returned no results",
521                           backend, pattern)
522                }
523            }
524        }
525    }
526}
527
528
529/// A callback mechanism to indicate what a backend is doing.
530///
531/// We use an enum instead of a separate function for each message so
532/// that a naive listener can just print each message to stdout.
533///
534/// This is primarily interesting for backends like a keyserver.
535///
536/// Currently, backends are not required to implement this trait.  In
537/// fact [`KeyServer`] is the only backend that implements it.  A
538/// caller can add a listener to a `KeyServer` using
539/// [`KeyServer::add_listener`].
540pub trait StatusListener {
541    /// A status update.
542    fn update(&self, status: &StatusUpdate);
543}
544
545/// Returns certificates from a backing store.
546pub trait Store<'a> {
547    /// Returns the certificates whose fingerprint matches the handle.
548    ///
549    /// Returns [`StoreError::NotFound`] if no certificate is found.
550    ///
551    /// The caller may assume that looking up a fingerprint returns at
552    /// most one certificate.
553    fn lookup_by_cert(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>>;
554
555    /// Returns the certificate with the specified fingerprint, if any.
556    ///
557    /// Returns [`StoreError::NotFound`] if the certificate is not found.
558    ///
559    /// The default implementation is implemented in terms of
560    /// [`Store::lookup_by_cert`].
561    fn lookup_by_cert_fpr(&self, fingerprint: &Fingerprint)
562        -> Result<Arc<LazyCert<'a>>>
563    {
564        let kh = KeyHandle::from(fingerprint.clone());
565
566        self.lookup_by_cert(&kh)
567            .and_then(|v| {
568                assert!(v.len() <= 1,
569                        "Looking up {} returned multiple certificates: {}",
570                        fingerprint,
571                        v.iter()
572                            .map(|c| {
573                                c.fingerprint().to_string()
574                            })
575                            .collect::<Vec<String>>()
576                            .join(", "));
577                v.into_iter().next()
578                    .ok_or(StoreError::NotFound(kh).into())
579            })
580    }
581
582    /// Returns certificates that have a key with the specified
583    /// handle, if any.
584    ///
585    /// Returns [`StoreError::NotFound`] if no certificate is not found.
586    ///
587    /// Note: even if you pass a fingerprint, this may return multiple
588    /// certificates as the same subkey may be attached to multiple
589    /// certificates.
590    fn lookup_by_cert_or_subkey(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>>;
591
592    /// Returns certificates that have a User ID matching the
593    /// specified pattern according to the query parameters.
594    ///
595    /// User IDs are interpreted as UTF-8 strings.  If a user ID is
596    /// not valid UTF-8, then it will be converted to UTF-8 in a lossy
597    /// manner using the same semantics as
598    /// [`String::from_utf8_lossy`].
599    ///
600    /// This function returns all matching user IDs, even those that
601    /// are not self signed.  It's up to the caller to authenticate
602    /// the returned user IDs.
603    fn select_userid(&self, query: &UserIDQueryParams, pattern: &str)
604        -> Result<Vec<Arc<LazyCert<'a>>>>;
605
606    /// Performs an exact match on the User ID.
607    ///
608    /// The pattern is anchored, and the match is case sensitive.
609    ///
610    /// The user ID is interpreted as a UTF-8 string.  If the user ID
611    /// is not valid UTF-8, then it will be converted to UTF-8 in a
612    /// lossy manner using the same semantics as
613    /// [`String::from_utf8_lossy`].
614    ///
615    /// This function returns all matching user IDs, even those that
616    /// are not self signed.  It's up to the caller to authenticate
617    /// the returned user IDs.
618    fn lookup_by_userid(&self, userid: &UserID) -> Result<Vec<Arc<LazyCert<'a>>>> {
619        self.select_userid(
620            &UserIDQueryParams::new()
621                .set_email(false)
622                .set_anchor_start(true)
623                .set_anchor_end(true)
624                .set_ignore_case(false),
625            &String::from_utf8_lossy(userid.value()))
626    }
627
628    /// Performs a case insenitive, substring match on the User ID.
629    ///
630    /// The pattern is not anchored, and it is matched case
631    /// insensitively.
632    ///
633    /// User IDs are interpreted as UTF-8 strings.  If a user ID is
634    /// not valid UTF-8, then it will be converted to UTF-8 in a lossy
635    /// manner using the same semantics as
636    /// [`String::from_utf8_lossy`].
637    ///
638    /// This function returns all matching user IDs, even those that
639    /// are not self signed.  It's up to the caller to authenticate
640    /// the returned user IDs.
641    fn grep_userid(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
642        self.select_userid(
643            &UserIDQueryParams::new()
644                .set_email(false)
645                .set_anchor_start(false)
646                .set_anchor_end(false)
647                .set_ignore_case(true),
648            pattern)
649    }
650
651    /// Returns certificates that have a User ID with the specified
652    /// email address.
653    ///
654    /// The pattern is interpreted as an email address.  It is first
655    /// normalized, and then matched against the normalized email
656    /// address, it is anchored, and the match is case sensitive.
657    ///
658    /// User IDs are interpreted as UTF-8 strings.  If a user ID is
659    /// not valid UTF-8, then it will be converted to UTF-8 in a lossy
660    /// manner using the same semantics as
661    /// [`String::from_utf8_lossy`].
662    ///
663    /// This function returns all matching user IDs, even those that
664    /// are not self signed.  It's up to the caller to authenticate
665    /// the returned user IDs.
666    fn lookup_by_email(&self, email: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
667        let userid = crate::email_to_userid(&email)?;
668        let email = userid.email_normalized()?.expect("have one");
669
670        self.select_userid(
671            &UserIDQueryParams::new()
672                .set_email(true)
673                .set_anchor_start(true)
674                .set_anchor_end(true)
675                .set_ignore_case(false),
676            &email)
677    }
678
679    /// Performs a case insenitive, substring match on the normalized
680    /// email address.
681    ///
682    /// The pattern is matched against the normalized email address,
683    /// it is not anchored, and it is matched case insensitively.  The
684    /// pattern itself is *not* normalized.
685    ///
686    /// User IDs are interpreted as UTF-8 strings.  If a user ID is
687    /// not valid UTF-8, then it will be converted to UTF-8 in a lossy
688    /// manner using the same semantics as
689    /// [`String::from_utf8_lossy`].
690    ///
691    /// This function returns all matching user IDs, even those that
692    /// are not self signed.  It's up to the caller to authenticate
693    /// the returned user IDs.
694    fn grep_email(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
695        self.select_userid(
696            &UserIDQueryParams::new()
697                .set_email(true)
698                .set_anchor_start(false)
699                .set_anchor_end(false)
700                .set_ignore_case(true),
701            pattern)
702    }
703
704    /// Returns certificates that have User ID with an email address
705    /// from the specified domain.
706    ///
707    /// The pattern is interpreted as a domain address.  It is first
708    /// normalized, and then matched against the normalized email
709    /// address, it is anchored, and the match is case sensitive.
710    ///
711    /// `domain` must be a bare domain, like `example.org`; it should
712    /// not start with an `@`.  This does not match subdomains.  That
713    /// is, it will match `alice@foo.bar.com` when searching for
714    /// `bar.com`.
715    ///
716    /// User IDs are interpreted as UTF-8 strings.  If a user ID is
717    /// not valid UTF-8, then it will be converted to UTF-8 in a lossy
718    /// manner using the same semantics as
719    /// [`String::from_utf8_lossy`].
720    ///
721    /// This function returns all matching user IDs, even those that
722    /// are not self signed.  It's up to the caller to authenticate
723    /// the returned user IDs.
724    fn lookup_by_email_domain(&self, domain: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
725        let localpart = "localpart";
726        let email = format!("{}@{}", localpart, domain);
727        let userid = crate::email_to_userid(&email)?;
728        let email = userid.email_normalized()?.expect("have one");
729        let domain = &email[email.rfind('@').expect("have an @")..];
730
731        self.select_userid(
732            &UserIDQueryParams::new()
733                .set_email(true)
734                .set_anchor_start(false)
735                .set_anchor_end(true)
736                .set_ignore_case(false),
737            domain)
738    }
739
740    /// Lists all of the certificates.
741    ///
742    /// If a backend is not able to enumerate all the certificates,
743    /// then it should return those that it knows about.  For
744    /// instance, some keyservers allow certificates to be looked up
745    /// by fingerprint, but not to enumerate all of the certificates.
746    /// Thus, a user must not assume that if a certificate is not
747    /// returned by this function, it cannot be found by name.
748    fn fingerprints<'b>(&'b self) -> Box<dyn Iterator<Item=Fingerprint> + 'b>;
749
750    /// Returns all of the certificates.
751    ///
752    /// The default implementation is implemented in terms of
753    /// [`Store::fingerprints`] and [`Store::lookup_by_cert_fpr`].  Many backends
754    /// will be able to do this more efficiently.
755    fn certs<'b>(&'b self)
756        -> Box<dyn Iterator<Item=Arc<LazyCert<'a>>> + 'b>
757        where 'a: 'b
758    {
759        Box::new(self.fingerprints()
760            .filter_map(|fpr| {
761                self.lookup_by_cert_fpr(&fpr).ok()
762            }))
763    }
764
765    /// Prefills the cache.
766    ///
767    /// Prefilling the cache makes sense when you plan to examine most
768    /// certificates.  It doesn't make sense if you are just
769    /// authenticating a single or a few bindings.
770    ///
771    /// This function may be multi-threaded.
772    ///
773    /// Errors should be silently ignored and propagated when the
774    /// operation in question is executed directly.
775    fn prefetch_all(&self) {
776    }
777
778    /// Prefetches some certificates.
779    ///
780    /// Prefilling the cache makes sense when you plan to examine some
781    /// certificates in the near future.
782    ///
783    /// This interface is useful as it allows batching, which may be
784    /// more efficient, especially when the certificates are accessed
785    /// over the network.  And, the function may be multi-threaded.
786    ///
787    /// Errors should be silently ignored and propagated when the
788    /// operation in question is executed directly.
789    fn prefetch_some(&self, certs: &[KeyHandle]) {
790        let _ = certs;
791    }
792}
793
794// The references in Store need a different lifetime from the contents
795// of the Box.  Otherwise, a `Backend` that is a `&Box<Store>` would
796// create a self referential data structure.
797impl<'a: 't, 't, T> Store<'a> for Box<T>
798where T: Store<'a> + ?Sized + 't
799{
800    fn lookup_by_cert(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
801        self.as_ref().lookup_by_cert(kh)
802    }
803
804    fn lookup_by_cert_fpr(&self, fingerprint: &Fingerprint)
805        -> Result<Arc<LazyCert<'a>>>
806    {
807        self.as_ref().lookup_by_cert_fpr(fingerprint)
808    }
809
810    fn lookup_by_cert_or_subkey(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
811        self.as_ref().lookup_by_cert_or_subkey(kh)
812    }
813
814    fn select_userid(&self, query: &UserIDQueryParams, pattern: &str)
815        -> Result<Vec<Arc<LazyCert<'a>>>>
816    {
817        self.as_ref().select_userid(query, pattern)
818    }
819
820    fn lookup_by_userid(&self, userid: &UserID) -> Result<Vec<Arc<LazyCert<'a>>>> {
821        self.as_ref().lookup_by_userid(userid)
822    }
823
824    fn grep_userid(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
825        self.as_ref().grep_userid(pattern)
826    }
827
828    fn lookup_by_email(&self, email: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
829        self.as_ref().lookup_by_email(email)
830    }
831
832    fn grep_email(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
833        self.as_ref().grep_email(pattern)
834    }
835
836    fn lookup_by_email_domain(&self, domain: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
837        self.as_ref().lookup_by_email_domain(domain)
838    }
839
840    fn fingerprints<'b>(&'b self) -> Box<dyn Iterator<Item=Fingerprint> + 'b> {
841        self.as_ref().fingerprints()
842    }
843
844    fn certs<'b>(&'b self)
845        -> Box<dyn Iterator<Item=Arc<LazyCert<'a>>> + 'b>
846        where 'a: 'b
847    {
848        self.as_ref().certs()
849    }
850
851    fn prefetch_all(&self) {
852        self.as_ref().prefetch_all()
853    }
854
855    fn prefetch_some(&self, certs: &[KeyHandle]) {
856        self.as_ref().prefetch_some(certs)
857    }
858}
859
860impl<'a: 't, 't, T> Store<'a> for &'t T
861where T: Store<'a> + ?Sized
862{
863    fn lookup_by_cert(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
864        (*self).lookup_by_cert(kh)
865    }
866
867    fn lookup_by_cert_fpr(&self, fingerprint: &Fingerprint)
868        -> Result<Arc<LazyCert<'a>>>
869    {
870        (*self).lookup_by_cert_fpr(fingerprint)
871    }
872
873    fn lookup_by_cert_or_subkey(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
874        (*self).lookup_by_cert_or_subkey(kh)
875    }
876
877    fn select_userid(&self, query: &UserIDQueryParams, pattern: &str)
878        -> Result<Vec<Arc<LazyCert<'a>>>>
879    {
880        (*self).select_userid(query, pattern)
881    }
882
883    fn lookup_by_userid(&self, userid: &UserID) -> Result<Vec<Arc<LazyCert<'a>>>> {
884        (*self).lookup_by_userid(userid)
885    }
886
887    fn grep_userid(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
888        (*self).grep_userid(pattern)
889    }
890
891    fn lookup_by_email(&self, email: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
892        (*self).lookup_by_email(email)
893    }
894
895    fn grep_email(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
896        (*self).grep_email(pattern)
897    }
898
899    fn lookup_by_email_domain(&self, domain: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
900        (*self).lookup_by_email_domain(domain)
901    }
902
903    fn fingerprints<'b>(&'b self) -> Box<dyn Iterator<Item=Fingerprint> + 'b> {
904        (*self).fingerprints()
905    }
906
907    fn certs<'b>(&'b self)
908        -> Box<dyn Iterator<Item=Arc<LazyCert<'a>>> + 'b>
909        where 'a: 'b
910    {
911        (*self).certs()
912    }
913}
914
915impl<'a: 't, 't, T> Store<'a> for &'t mut T
916where T: Store<'a> + ?Sized
917{
918    fn lookup_by_cert(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
919        (**self).lookup_by_cert(kh)
920    }
921
922    fn lookup_by_cert_fpr(&self, fingerprint: &Fingerprint)
923        -> Result<Arc<LazyCert<'a>>>
924    {
925        (**self).lookup_by_cert_fpr(fingerprint)
926    }
927
928    fn lookup_by_cert_or_subkey(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
929        (**self).lookup_by_cert_or_subkey(kh)
930    }
931
932    fn select_userid(&self, query: &UserIDQueryParams, pattern: &str)
933        -> Result<Vec<Arc<LazyCert<'a>>>>
934    {
935        (**self).select_userid(query, pattern)
936    }
937
938    fn lookup_by_userid(&self, userid: &UserID) -> Result<Vec<Arc<LazyCert<'a>>>> {
939        (**self).lookup_by_userid(userid)
940    }
941
942    fn grep_userid(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
943        (**self).grep_userid(pattern)
944    }
945
946    fn lookup_by_email(&self, email: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
947        (**self).lookup_by_email(email)
948    }
949
950    fn grep_email(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
951        (**self).grep_email(pattern)
952    }
953
954    fn lookup_by_email_domain(&self, domain: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
955        (**self).lookup_by_email_domain(domain)
956    }
957
958    fn fingerprints<'b>(&'b self) -> Box<dyn Iterator<Item=Fingerprint> + 'b> {
959        (**self).fingerprints()
960    }
961
962    fn certs<'b>(&'b self)
963        -> Box<dyn Iterator<Item=Arc<LazyCert<'a>>> + 'b>
964        where 'a: 'b
965    {
966        (**self).certs()
967    }
968
969    fn prefetch_all(&self) {
970        (**self).prefetch_all()
971    }
972
973    fn prefetch_some(&self, certs: &[KeyHandle]) {
974        (**self).prefetch_some(certs)
975    }
976}
977
978impl<'a, T> Store<'a> for Arc<T>
979where T: Store<'a> + ?Sized
980{
981    fn lookup_by_cert(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
982        self.as_ref().lookup_by_cert(kh)
983    }
984
985    fn lookup_by_cert_fpr(&self, fingerprint: &Fingerprint)
986        -> Result<Arc<LazyCert<'a>>>
987    {
988        self.as_ref().lookup_by_cert_fpr(fingerprint)
989    }
990
991    fn lookup_by_cert_or_subkey(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
992        self.as_ref().lookup_by_cert_or_subkey(kh)
993    }
994
995    fn select_userid(&self, query: &UserIDQueryParams, pattern: &str)
996        -> Result<Vec<Arc<LazyCert<'a>>>>
997    {
998        self.as_ref().select_userid(query, pattern)
999    }
1000
1001    fn lookup_by_userid(&self, userid: &UserID) -> Result<Vec<Arc<LazyCert<'a>>>> {
1002        self.as_ref().lookup_by_userid(userid)
1003    }
1004
1005    fn grep_userid(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
1006        self.as_ref().grep_userid(pattern)
1007    }
1008
1009    fn lookup_by_email(&self, email: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
1010        self.as_ref().lookup_by_email(email)
1011    }
1012
1013    fn grep_email(&self, pattern: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
1014        self.as_ref().grep_email(pattern)
1015    }
1016
1017    fn lookup_by_email_domain(&self, domain: &str) -> Result<Vec<Arc<LazyCert<'a>>>> {
1018        self.as_ref().lookup_by_email_domain(domain)
1019    }
1020
1021    fn fingerprints<'b>(&'b self) -> Box<dyn Iterator<Item=Fingerprint> + 'b> {
1022        self.as_ref().fingerprints()
1023    }
1024
1025    fn certs<'b>(&'b self)
1026        -> Box<dyn Iterator<Item=Arc<LazyCert<'a>>> + 'b>
1027        where 'a: 'b
1028    {
1029        self.as_ref().certs()
1030    }
1031}
1032
1033/// Merges two certificates.
1034///
1035/// This is primarily useful as the `merge_strategy` callback to
1036/// [`StoreUpdate::update_by`].
1037pub trait MergeCerts<'a> {
1038    /// Merges two certificates.
1039    ///
1040    /// This is primarily useful as the `merge_strategy` callback to
1041    /// [`StoreUpdate::update_by`].
1042    ///
1043    /// The default implementation merges the two certificates using
1044    /// [`Cert::merge_public`].  This means that any secret key
1045    /// material in `disk` is preserved, any secret key material in
1046    /// `new` is ignored, and unhashed subpacket areas are merged.
1047    ///
1048    /// Note: `self` is a normal reference, not a mutable reference.
1049    /// This means that the underlying implementation has to use
1050    /// interior mutability.  The advantage is that multiple threads
1051    /// can have a reference to the store, and update it in parallel.
1052    fn merge_public<'b>(&self,
1053                        new: Arc<LazyCert<'a>>,
1054                        disk: Option<Arc<LazyCert<'b>>>)
1055        -> Result<Arc<LazyCert<'a>>>
1056    {
1057        if let Some(disk) = disk {
1058            let merged = disk.to_cert()?.clone()
1059                .merge_public(new.to_cert()?.clone())?;
1060            Ok(Arc::new(LazyCert::from(merged)))
1061        } else {
1062            if new.is_tsk() {
1063                Ok(Arc::new(LazyCert::from(new.to_cert()?.clone()
1064                                           .strip_secret_key_material())))
1065            } else {
1066                Ok(new)
1067            }
1068        }
1069    }
1070}
1071
1072impl<'a> MergeCerts<'a> for () {
1073}
1074
1075/// Provides an interface to update a backing store.
1076pub trait StoreUpdate<'a>: Store<'a> {
1077    /// Insert a certificate.
1078    ///
1079    /// This uses the default implementation of [`MergeCerts`] to
1080    /// merge the certificate with any existing certificate.
1081    ///
1082    /// Note: `self` is a normal reference, not a mutable reference.
1083    /// This means that the underlying implementation has to use
1084    /// interior mutability.  The advantage is that multiple threads
1085    /// can have a reference to the store, and update it in parallel.
1086    fn update(&self, cert: Arc<LazyCert<'a>>) -> Result<()> {
1087        self.update_by(cert, &mut ())?;
1088
1089        Ok(())
1090    }
1091
1092    /// Inserts a certificate into the store.
1093    ///
1094    /// Inserts a certificate into the store and uses `merge_strategy`
1095    /// to merge it with the existing certificate, if any.
1096    ///
1097    /// Unless there is an error, you must call `merge_strategy`.
1098    /// This is the case even if the certificate is not on the
1099    /// backend.  In that case, you must pass `None` for the on-disk
1100    /// version.  This allows `merge_strategy` to generate statistics,
1101    /// and to modify the certificate before it is saved, e.g., by
1102    /// stripping third-party certifications.
1103    ///
1104    /// To use the default merge strategy, either call
1105    /// [`StoreUpdate::update`] directly, or pass `&mut ()`.
1106    ///
1107    /// Note: `self` and `merge_strategy` are normal references, not a
1108    /// mutable reference.  This means that the underlying
1109    /// implementation has to use interior mutability.  The advantage
1110    /// is that multiple threads can have a reference to the store,
1111    /// and update it in parallel.
1112    fn update_by(&self, cert: Arc<LazyCert<'a>>,
1113                 merge_strategy: &dyn MergeCerts<'a>)
1114        -> Result<Arc<LazyCert<'a>>>;
1115}
1116
1117impl<'a: 't, 't, T> StoreUpdate<'a> for Box<T>
1118where T: StoreUpdate<'a> + ?Sized + 't
1119{
1120    fn update(&self, cert: Arc<LazyCert<'a>>) -> Result<()> {
1121        self.as_ref().update(cert)
1122    }
1123
1124    fn update_by(&self, cert: Arc<LazyCert<'a>>,
1125                 merge_strategy: &dyn MergeCerts<'a>)
1126        -> Result<Arc<LazyCert<'a>>>
1127    {
1128        self.as_ref().update_by(cert, merge_strategy)
1129    }
1130}
1131
1132impl<'a: 't, 't, T> StoreUpdate<'a> for &'t T
1133where T: StoreUpdate<'a> + ?Sized
1134{
1135    fn update(&self, cert: Arc<LazyCert<'a>>) -> Result<()> {
1136        (*self).update(cert)
1137    }
1138
1139    fn update_by(&self, cert: Arc<LazyCert<'a>>,
1140                 merge_strategy: &dyn MergeCerts<'a>)
1141        -> Result<Arc<LazyCert<'a>>>
1142    {
1143        (*self).update_by(cert, merge_strategy)
1144    }
1145}
1146
1147impl<'a, T> StoreUpdate<'a> for Arc<T>
1148where T: StoreUpdate<'a> + ?Sized
1149{
1150    fn update(&self, cert: Arc<LazyCert<'a>>) -> Result<()> {
1151        self.as_ref().update(cert)
1152    }
1153
1154    fn update_by(&self, cert: Arc<LazyCert<'a>>,
1155                 merge_strategy: &dyn MergeCerts<'a>)
1156        -> Result<Arc<LazyCert<'a>>>
1157    {
1158        self.as_ref().update_by(cert, merge_strategy)
1159    }
1160}
1161
1162/// Merges two certificates and collects statistics.
1163///
1164/// This is primarily useful as the `merge_strategy` callback to
1165/// [`StoreUpdate::update_by`].
1166#[derive(Debug)]
1167#[non_exhaustive]
1168pub struct MergePublicCollectStats {
1169    /// Number of new certificates.
1170    new_certs: AtomicUsize,
1171
1172    /// Number of unchanged certificates.
1173    ///
1174    /// Note: there may be false negative.  That is some certificates
1175    /// may be unchanged, but the heuristic thinks that they have been
1176    /// updated.
1177    unchanged_certs: AtomicUsize,
1178
1179    /// Number of update certificates.
1180    updated_certs: AtomicUsize,
1181
1182    /// Number of errors.
1183    errors: AtomicUsize,
1184}
1185assert_send_and_sync!(MergePublicCollectStats);
1186
1187impl MergePublicCollectStats {
1188    /// Returns a new `MergePublicCollectStats` with all stats set to 0.
1189    pub fn new() -> Self {
1190        Self {
1191            new_certs: AtomicUsize::new(0),
1192            unchanged_certs: AtomicUsize::new(0),
1193            updated_certs: AtomicUsize::new(0),
1194            errors: AtomicUsize::new(0),
1195        }
1196    }
1197
1198    /// Returns the number of new certificates.
1199    pub fn new_certs(&self) -> usize {
1200        self.new_certs.load(Ordering::Relaxed)
1201    }
1202
1203    /// Returns the number of unchanged certificates.
1204    pub fn unchanged_certs(&self) -> usize {
1205        self.unchanged_certs.load(Ordering::Relaxed)
1206    }
1207
1208    /// Returns the number of updated certificates.
1209    pub fn updated_certs(&self) -> usize {
1210        self.updated_certs.load(Ordering::Relaxed)
1211    }
1212
1213    /// Returns the number of errors.
1214    pub fn errors(&self) -> usize {
1215        self.errors.load(Ordering::Relaxed)
1216    }
1217
1218    /// Increments the number of new certificates by 1.
1219    pub fn inc_new_certs(&self) {
1220        self.new_certs.fetch_add(1, Ordering::Relaxed);
1221    }
1222
1223    /// Increments the number of unchanged certificates by 1.
1224    pub fn inc_unchanged_certs(&self) {
1225        self.unchanged_certs.fetch_add(1, Ordering::Relaxed);
1226    }
1227
1228    /// Increments the number of updated certificates by 1.
1229    pub fn inc_updated_certs(&self) {
1230        self.updated_certs.fetch_add(1, Ordering::Relaxed);
1231    }
1232
1233    /// Increments the number of errors by 1.
1234    pub fn inc_errors(&self) {
1235        self.errors.fetch_add(1, Ordering::Relaxed);
1236    }
1237}
1238
1239impl<'a> MergeCerts<'a> for MergePublicCollectStats {
1240    /// Merges two certificates.
1241    ///
1242    /// This is primarily useful as the `merge_strategy` callback to
1243    /// [`StoreUpdate::update_by`].
1244    ///
1245    /// This implementation has the same merge semantics as the
1246    /// default implementation, but it also updates the statistics in
1247    /// `self`.
1248    ///
1249    /// # Examples
1250    ///
1251    /// ```rust
1252    /// use std::any::Any;
1253    /// use std::sync::Arc;
1254    ///
1255    /// use sequoia_openpgp as openpgp;
1256    /// # use openpgp::Result;
1257    /// use openpgp::cert::prelude::*;
1258    /// use openpgp::parse::Parse;
1259    ///
1260    /// use sequoia_cert_store as cert_store;
1261    /// use cert_store::CertStore;
1262    /// use cert_store::LazyCert;
1263    /// use cert_store::store::MergePublicCollectStats;
1264    /// use cert_store::store::StoreUpdate;
1265    ///
1266    /// # fn main() -> Result<()> {
1267    /// let (cert, _rev) = CertBuilder::new().generate()?;
1268    ///
1269    /// let mut certs = CertStore::empty();
1270    ///
1271    /// let mut stats = MergePublicCollectStats::new();
1272    ///
1273    /// certs.update_by(Arc::new(LazyCert::from(cert)), &mut stats)
1274    ///         .expect("valid");
1275    ///
1276    /// assert_eq!(stats.new_certs(), 1);
1277    /// # Ok(()) }
1278    /// ```
1279    fn merge_public<'b, 'rb>(&self,
1280                             new: Arc<LazyCert<'a>>,
1281                             disk: Option<Arc<LazyCert<'b>>>)
1282        -> Result<Arc<LazyCert<'a>>>
1283    {
1284        let disk = if let Some(disk) = disk {
1285            disk
1286        } else {
1287            self.inc_new_certs();
1288
1289            if new.is_tsk() {
1290                return Ok(Arc::new(LazyCert::from(
1291                    new.to_cert()?.clone().strip_secret_key_material())))
1292            } else {
1293                return Ok(new);
1294            }
1295        };
1296
1297        let fpr = new.fingerprint();
1298
1299        let disk = disk.to_cert()
1300            .with_context(|| {
1301                format!("Parsing {} as returned from the cert directory", fpr)
1302            })?
1303            .clone();
1304
1305        let new = new.to_cert()
1306            .with_context(|| {
1307                format!("Parsing {} as being inserted into \
1308                         the cert directory",
1309                        fpr)
1310            })?
1311            .clone();
1312
1313        if disk == new {
1314            self.inc_unchanged_certs();
1315            Ok(Arc::new(LazyCert::from(new)))
1316        } else {
1317            // If the on-disk version has secrets, we preserve them.
1318            // If new has secrets, we ignore them.
1319            match disk.clone().merge_public(new) {
1320                Ok(merged) => {
1321                    if merged == disk {
1322                        self.inc_unchanged_certs();
1323                    } else {
1324                        self.inc_updated_certs();
1325                    }
1326                    Ok(Arc::new(LazyCert::from(merged)))
1327                }
1328                Err(err) => {
1329                    self.inc_errors();
1330                    Err(err.into())
1331                }
1332            }
1333        }
1334    }
1335}
1336
1337#[cfg(test)]
1338mod tests {
1339    #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
1340    use wasm_bindgen_test::wasm_bindgen_test as test;
1341
1342    use super::*;
1343
1344    use crate::store;
1345
1346    // Make sure we can pass a &Box<Store> where a generic type
1347    // needs to implement Store.
1348    #[test]
1349    fn store_boxed() -> Result<()> {
1350        struct Foo<'a, B>
1351        where B: Store<'a>
1352        {
1353            backend: B,
1354            _a: std::marker::PhantomData<&'a ()>,
1355        }
1356
1357        impl<'a, B> Foo<'a, B>
1358        where B: Store<'a>
1359        {
1360            fn new(backend: B) -> Self
1361            {
1362                Foo {
1363                    backend,
1364                    _a: std::marker::PhantomData,
1365                }
1366            }
1367
1368            fn count(&self) -> usize {
1369                self.backend.certs().count()
1370            }
1371        }
1372
1373        let backend = store::Certs::empty();
1374        let backend: Box<dyn Store> = Box::new(backend);
1375        let foo = Foo::new(&backend);
1376
1377        // Do something (anything) with the backend.
1378        assert_eq!(foo.count(), 0);
1379
1380        Ok(())
1381    }
1382
1383    // Make sure we can pass a &Box<StoreUpdate> where a generic type
1384    // needs to implement Store.
1385    #[test]
1386    fn store_update_boxed() -> Result<()> {
1387        struct Foo<'a, B>
1388        where B: StoreUpdate<'a>
1389        {
1390            backend: B,
1391            _a: std::marker::PhantomData<&'a ()>,
1392        }
1393
1394        impl<'a, B> Foo<'a, B>
1395        where B: StoreUpdate<'a>
1396        {
1397            fn new(backend: B) -> Self
1398            {
1399                Foo {
1400                    backend,
1401                    _a: std::marker::PhantomData,
1402                }
1403            }
1404
1405            fn count(&self) -> usize {
1406                self.backend.certs().count()
1407            }
1408        }
1409
1410        let backend = store::Certs::empty();
1411        let backend: Box<dyn StoreUpdate> = Box::new(backend);
1412        let foo = Foo::new(&backend);
1413
1414        // Do something (anything) with the backend.
1415        assert_eq!(foo.count(), 0);
1416
1417        Ok(())
1418    }
1419
1420    #[test]
1421    fn is_email() {
1422        assert!(UserIDQueryParams::is_email("foo@domain.com").is_ok());
1423
1424        // Need a local part.
1425        assert!(UserIDQueryParams::is_email("@domain.com").is_err());
1426        // Need a domain.
1427        assert!(UserIDQueryParams::is_email("foo@").is_err());
1428
1429        // One @
1430        assert!(UserIDQueryParams::is_email("foo").is_err());
1431        assert!(UserIDQueryParams::is_email("foo@@domain.com").is_err());
1432        assert!(UserIDQueryParams::is_email("foo@a@domain.com").is_err());
1433
1434        // Bare email address, not wrapped in angle brackets.
1435        assert!(UserIDQueryParams::is_email("<foo@domain.com>").is_err());
1436
1437        // Whitespace is not allowed.
1438        assert!(UserIDQueryParams::is_email(" foo@domain.com").is_err());
1439        assert!(UserIDQueryParams::is_email("foo o@domain.com").is_err());
1440        assert!(UserIDQueryParams::is_email("foo@do main.com").is_err());
1441        assert!(UserIDQueryParams::is_email("foo@domain.com ").is_err());
1442    }
1443
1444    #[test]
1445    fn is_domain() {
1446        assert!(UserIDQueryParams::is_domain("domain.com").is_ok());
1447
1448        // No at.
1449        assert!(UserIDQueryParams::is_domain("foo").is_ok());
1450        assert!(UserIDQueryParams::is_domain("@domain.com").is_err());
1451        assert!(UserIDQueryParams::is_domain("foo@").is_err());
1452        assert!(UserIDQueryParams::is_domain("foo@@domain.com").is_err());
1453        assert!(UserIDQueryParams::is_domain("foo@a@domain.com").is_err());
1454        assert!(UserIDQueryParams::is_domain("<foo@domain.com>").is_err());
1455    }
1456
1457    include!("../tests/keyring.rs");
1458
1459    // Check that MergePublicCollectStats works as advertised.
1460    #[test]
1461    fn store_update_merge_public_collect_stats() {
1462        use std::collections::HashSet;
1463
1464        use openpgp::Cert;
1465        use openpgp::parse::Parse;
1466
1467        use crate::CertStore;
1468        use crate::store::MergePublicCollectStats;
1469
1470        let certs = CertStore::empty();
1471
1472        let mut stats = MergePublicCollectStats::new();
1473
1474        let mut seen = HashSet::new();
1475
1476        for (i, cert) in keyring::certs.iter().enumerate() {
1477            let cert = Cert::from_bytes(&cert.bytes()).expect("valid");
1478            let fpr = cert.fingerprint();
1479            seen.insert(fpr.clone());
1480
1481            certs.update_by(Arc::new(LazyCert::from(cert)), &mut stats)
1482                .expect("valid");
1483
1484            eprintln!("After inserting {} ({}), stats: {:?}",
1485                      i, fpr, stats);
1486
1487            assert_eq!(stats.new_certs(), seen.len());
1488            assert_eq!(stats.new_certs()
1489                       + stats.updated_certs()
1490                       + stats.unchanged_certs(),
1491                       i + 1);
1492        }
1493
1494        let new = stats.new_certs();
1495        let updated = stats.updated_certs();
1496        let unchanged = stats.unchanged_certs();
1497
1498        // Insert again.  This time nothing should change.
1499        for (i, cert) in keyring::certs.iter().enumerate() {
1500            let cert = Cert::from_bytes(&cert.bytes()).expect("valid");
1501            let fpr = cert.fingerprint();
1502
1503            certs.update_by(Arc::new(LazyCert::from(cert)), &mut stats)
1504                .expect("valid");
1505
1506            eprintln!("After reinserting {} ({}), stats: {:?}",
1507                      i, fpr, stats);
1508
1509            // These should not change:
1510            assert_eq!(stats.new_certs(), new);
1511            // Update should also not change, but there may be false
1512            // positives.
1513            assert_eq!(stats.unchanged_certs() + stats.updated_certs(),
1514                       updated + unchanged + i + 1);
1515        }
1516    }
1517}