1use crate::{
4 discover::{AsDiscovered, DiscoveredAdvisory, DiscoveredContext, DiscoveredVisitor},
5 source::Source,
6};
7use bytes::Bytes;
8use sha2::{Sha256, Sha512};
9use std::{
10 fmt::Debug,
11 future::Future,
12 ops::{Deref, DerefMut},
13};
14use url::Url;
15use walker_common::retrieve::RetrievedDocument;
16use walker_common::{
17 retrieve::{RetrievalError, RetrievalMetadata, RetrievedDigest},
18 utils::{openpgp::PublicKey, url::Urlify},
19 validate::source::{KeySource, KeySourceError},
20};
21
22#[derive(Clone, Debug)]
24pub struct RetrievedAdvisory {
25 pub discovered: DiscoveredAdvisory,
27
28 pub data: Bytes,
30 pub signature: Option<String>,
32
33 pub sha256: Option<RetrievedDigest<Sha256>>,
35 pub sha512: Option<RetrievedDigest<Sha512>>,
37
38 pub metadata: RetrievalMetadata,
40}
41
42impl Urlify for RetrievedAdvisory {
43 fn url(&self) -> &Url {
44 &self.url
45 }
46
47 fn relative_base_and_url(&self) -> Option<(&Url, String)> {
48 self.discovered.relative_base_and_url()
49 }
50}
51
52pub trait AsRetrieved: Debug {
54 fn as_retrieved(&self) -> &RetrievedAdvisory;
55}
56
57impl AsDiscovered for RetrievedAdvisory {
58 fn as_discovered(&self) -> &DiscoveredAdvisory {
59 &self.discovered
60 }
61}
62
63impl AsRetrieved for RetrievedAdvisory {
64 fn as_retrieved(&self) -> &RetrievedAdvisory {
65 self
66 }
67}
68
69impl Deref for RetrievedAdvisory {
70 type Target = DiscoveredAdvisory;
71
72 fn deref(&self) -> &Self::Target {
73 &self.discovered
74 }
75}
76
77impl DerefMut for RetrievedAdvisory {
78 fn deref_mut(&mut self) -> &mut Self::Target {
79 &mut self.discovered
80 }
81}
82
83impl RetrievedDocument for RetrievedAdvisory {
84 type Discovered = DiscoveredAdvisory;
85}
86
87pub struct RetrievalContext<'c> {
88 pub discovered: &'c DiscoveredContext<'c>,
89 pub keys: &'c Vec<PublicKey>,
90}
91
92impl<'c> Deref for RetrievalContext<'c> {
93 type Target = DiscoveredContext<'c>;
94
95 fn deref(&self) -> &Self::Target {
96 self.discovered
97 }
98}
99
100pub trait RetrievedVisitor<S: Source> {
101 type Error: std::fmt::Display + Debug;
102 type Context;
103
104 fn visit_context(
105 &self,
106 context: &RetrievalContext,
107 ) -> impl Future<Output = Result<Self::Context, Self::Error>>;
108
109 fn visit_advisory(
110 &self,
111 context: &Self::Context,
112 result: Result<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>>,
113 ) -> impl Future<Output = Result<(), Self::Error>>;
114}
115
116impl<F, E, Fut, S> RetrievedVisitor<S> for F
117where
118 F: Fn(Result<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>>) -> Fut,
119 Fut: Future<Output = Result<(), E>>,
120 E: std::fmt::Display + Debug,
121 S: Source,
122{
123 type Error = E;
124 type Context = ();
125
126 async fn visit_context(
127 &self,
128 _context: &RetrievalContext<'_>,
129 ) -> Result<Self::Context, Self::Error> {
130 Ok(())
131 }
132
133 async fn visit_advisory(
134 &self,
135 _ctx: &Self::Context,
136 outcome: Result<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>>,
137 ) -> Result<(), Self::Error> {
138 self(outcome).await
139 }
140}
141
142pub struct RetrievingVisitor<V: RetrievedVisitor<S>, S: Source + KeySource> {
143 visitor: V,
144 source: S,
145}
146
147impl<V, S> RetrievingVisitor<V, S>
148where
149 V: RetrievedVisitor<S>,
150 S: Source + KeySource,
151{
152 pub fn new(source: S, visitor: V) -> Self {
153 Self { visitor, source }
154 }
155}
156
157#[derive(Debug, thiserror::Error)]
158pub enum Error<VE, SE, KSE>
159where
160 VE: std::fmt::Display + Debug,
161 SE: std::fmt::Display + Debug,
162 KSE: std::fmt::Display + Debug,
163{
164 #[error("Source error: {0}")]
165 Source(SE),
166 #[error("Key source error: {0}")]
167 KeySource(KeySourceError<KSE>),
168 #[error(transparent)]
169 Visitor(VE),
170}
171
172impl<V, S> DiscoveredVisitor for RetrievingVisitor<V, S>
173where
174 V: RetrievedVisitor<S>,
175 S: Source + KeySource,
176{
177 type Error =
178 Error<V::Error, <S as walker_common::source::Source>::Error, <S as KeySource>::Error>;
179 type Context = V::Context;
180
181 async fn visit_context(
182 &self,
183 context: &DiscoveredContext<'_>,
184 ) -> Result<Self::Context, Self::Error> {
185 let mut keys = Vec::with_capacity(context.metadata.public_openpgp_keys.len());
186
187 for key in &context.metadata.public_openpgp_keys {
188 keys.push(
189 self.source
190 .load_public_key(key.into())
191 .await
192 .map_err(Error::KeySource)?,
193 );
194 }
195
196 log::info!(
197 "Loaded {} public key{}",
198 keys.len(),
199 (keys.len() != 1).then_some("s").unwrap_or_default()
200 );
201 if log::log_enabled!(log::Level::Debug) {
202 for key in keys.iter().flat_map(|k| &k.certs) {
203 log::debug!(" {}", key.key_handle());
204 for id in key.userids() {
205 log::debug!(" {}", String::from_utf8_lossy(id.value()));
206 }
207 }
208 }
209
210 self.visitor
211 .visit_context(&RetrievalContext {
212 discovered: context,
213 keys: &keys,
214 })
215 .await
216 .map_err(Error::Visitor)
217 }
218
219 async fn visit_advisory(
220 &self,
221 context: &Self::Context,
222 discovered: DiscoveredAdvisory,
223 ) -> Result<(), Self::Error> {
224 let advisory = self
225 .source
226 .load_advisory(discovered.clone())
227 .await
228 .map_err(|err| RetrievalError::Source { err, discovered });
229
230 self.visitor
231 .visit_advisory(context, advisory)
232 .await
233 .map_err(Error::Visitor)?;
234
235 Ok(())
236 }
237}