diff --git a/src/lists.rs b/src/lists.rs
index ac51dea..6cb308b 100644
--- a/src/lists.rs
+++ b/src/lists.rs
@@ -183,7 +183,7 @@ pub fn selected_listings(release: &Release) -> Vec
{
}
if let Some(ref entry_arch) = entry.arch {
- if arch != entry_arch {
+ if !entry_arch.contains(arch) {
continue;
}
}
diff --git a/src/release.rs b/src/release.rs
index fe79dbb..6f02afc 100644
--- a/src/release.rs
+++ b/src/release.rs
@@ -38,6 +38,7 @@ pub struct RequestedRelease {
pub codename: String,
pub arches: Vec,
+ pub untrusted: bool,
}
#[derive(Debug, Clone)]
@@ -139,6 +140,7 @@ impl RequestedReleases {
mirror: Url::parse(&entry.url)?,
codename: entry.suite_codename.to_string(),
arches: arches.to_vec(),
+ untrusted: entry.untrusted,
}) {
hash_map::Entry::Vacant(vacancy) => {
vacancy.insert(vec![entry.clone()]);
@@ -173,7 +175,7 @@ impl RequestedReleases {
&dest,
)],
) {
- Ok(_) => gpg.verify_clearsigned(&dest, &verified),
+ Ok(_) => gpg.read_clearsigned(&dest, &verified, !release.untrusted),
Err(_) => {
let mut detatched_signature = dest.as_os_str().to_os_string();
detatched_signature.push(".gpg");
@@ -183,14 +185,18 @@ impl RequestedReleases {
&[Download::from_to(release.dists()?.join("Release")?, &dest)],
)?;
- fetch(
- client,
- &[Download::from_to(
- release.dists()?.join("Release.gpg")?,
- &detatched_signature,
- )],
- )?;
- gpg.verify_detached(&dest, detatched_signature, verified)
+ if !release.untrusted {
+ fetch(
+ client,
+ &[Download::from_to(
+ release.dists()?.join("Release.gpg")?,
+ &detatched_signature,
+ )],
+ )?;
+ gpg.verify_detached(&dest, detatched_signature, verified)
+ } else {
+ Ok(())
+ }
}
}
.with_context(|| anyhow!("verifying {:?} at {:?}", release, dest))?;
diff --git a/src/signing.rs b/src/signing.rs
index c1a7f3c..a1e2f8f 100644
--- a/src/signing.rs
+++ b/src/signing.rs
@@ -17,10 +17,11 @@ impl<'k> GpgClient<'k> {
GpgClient { keyring }
}
- pub fn verify_clearsigned, Q: AsRef>(
+ pub fn read_clearsigned, Q: AsRef>(
&self,
file: P,
dest: Q,
+ verify: bool,
) -> Result<(), Error> {
let from = fs::File::open(file).with_context(|| anyhow!("opening input file"))?;
let to = PersistableTempFile::new_in(
@@ -30,7 +31,12 @@ impl<'k> GpgClient<'k> {
)
.with_context(|| anyhow!("creating temporary file"))?;
- gpgrv::verify_message(io::BufReader::new(from), &to, &self.keyring)?;
+ let reader = io::BufReader::new(from);
+ if verify {
+ gpgrv::verify_message(reader, &to, &self.keyring)?;
+ } else {
+ gpgrv::read_doc(reader, &to)?;
+ }
to.persist_by_rename(dest)
.map_err(|e| e.error)
diff --git a/src/sources_list.rs b/src/sources_list.rs
index 91de552..200ba71 100644
--- a/src/sources_list.rs
+++ b/src/sources_list.rs
@@ -14,7 +14,30 @@ pub struct Entry {
pub url: String,
pub suite_codename: String,
pub components: Vec,
- pub arch: Option,
+ pub arch: Option>,
+ pub untrusted: bool,
+}
+
+#[derive(Default)]
+struct ParsedOpts {
+ arch: Option>,
+ untrusted: Option,
+}
+
+fn parse_opts(opt_parts: Vec<&str>) -> Result {
+ let mut ret = ParsedOpts::default();
+ for opt in opt_parts {
+ let parts: Vec<_> = opt.split("=").collect();
+ match parts.len() {
+ 2 => match parts[0] {
+ "arch" => ret.arch = Some(parts[1].split(',').map(|s| s.to_owned()).collect()),
+ "untrusted" => ret.untrusted = Some(parts[1] == "yes"),
+ other => bail!("unknown option: {}", other),
+ },
+ _ => bail!("multiple = in option"),
+ }
+ }
+ Ok(ret)
}
fn read_single_line(line: &str) -> Result, Error> {
@@ -33,14 +56,27 @@ fn read_single_line(line: &str) -> Result, Error> {
let src = parts
.next()
.ok_or_else(|| anyhow!("deb{{,s,-src}} section required"))?;
- let arch = match parts.peek() {
- Some(&val) if val.starts_with("[") => {
+
+ let mut opt_parts = vec![];
+ if let Some(&(mut val)) = parts.peek() {
+ if val.starts_with("[") {
+ val = val.strip_prefix("[").expect("opening [");
parts.next();
- Some(val)
+ loop {
+ if val.ends_with("]") {
+ opt_parts.push(val.strip_suffix("]").expect("closing ]"));
+ break;
+ }
+ opt_parts.push(val);
+ match parts.next() {
+ Some(next) => {
+ val = next;
+ }
+ None => bail!("unexpected end of line reading options"),
+ }
+ }
}
- Some(_) => None,
- None => bail!("unexpected end of line looking for arch or url"),
- };
+ }
let url = parts
.next()
@@ -60,6 +96,7 @@ fn read_single_line(line: &str) -> Result, Error> {
let mut ret = Vec::with_capacity(srcs.len());
+ let parsed_opts = parse_opts(opt_parts)?;
for src in srcs {
ret.push(Entry {
src: *src,
@@ -70,7 +107,8 @@ fn read_single_line(line: &str) -> Result, Error> {
},
suite_codename: suite.to_string(),
components: components.iter().map(|x| x.to_string()).collect(),
- arch: arch.map(|arch| arch.to_string()),
+ arch: parsed_opts.arch.clone(),
+ untrusted: parsed_opts.untrusted.unwrap_or(false),
});
}
@@ -113,6 +151,7 @@ mod tests {
url: "http://foo/".to_string(),
suite_codename: "bar".to_string(),
components: vec!["baz".to_string(), "quux".to_string()],
+ untrusted: false,
},
Entry {
src: true,
@@ -120,12 +159,113 @@ mod tests {
url: "http://foo/".to_string(),
suite_codename: "bar".to_string(),
components: vec!["baz".to_string(), "quux".to_string()],
+ untrusted: false,
},
],
read(io::Cursor::new(
r"
deb http://foo bar baz quux
deb-src http://foo bar baz quux
+",
+ ))
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn arch() {
+ assert_eq!(
+ vec![Entry {
+ src: false,
+ arch: Some(vec!["amd64".to_string()]),
+ url: "http://foo/".to_string(),
+ suite_codename: "bar".to_string(),
+ components: vec!["baz".to_string(), "quux".to_string()],
+ untrusted: false,
+ },],
+ read(io::Cursor::new(
+ r"
+deb [arch=amd64] http://foo bar baz quux
+",
+ ))
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn untrusted() {
+ assert_eq!(
+ vec![Entry {
+ src: false,
+ arch: None,
+ url: "http://foo/".to_string(),
+ suite_codename: "bar".to_string(),
+ components: vec!["baz".to_string(), "quux".to_string()],
+ untrusted: true,
+ },],
+ read(io::Cursor::new(
+ r"
+deb [untrusted=yes] http://foo bar baz quux
+",
+ ))
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn untrusted_not_yes() {
+ assert_eq!(
+ vec![Entry {
+ src: false,
+ arch: None,
+ url: "http://foo/".to_string(),
+ suite_codename: "bar".to_string(),
+ components: vec!["baz".to_string(), "quux".to_string()],
+ untrusted: false,
+ },],
+ read(io::Cursor::new(
+ r"
+deb [untrusted=yeppers] http://foo bar baz quux
+",
+ ))
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn multiple_opt() {
+ assert_eq!(
+ vec![Entry {
+ src: false,
+ arch: Some(vec!["amd64".to_string()]),
+ url: "http://foo/".to_string(),
+ suite_codename: "bar".to_string(),
+ components: vec!["baz".to_string(), "quux".to_string()],
+ untrusted: true,
+ },],
+ read(io::Cursor::new(
+ r"
+deb [untrusted=yes arch=amd64] http://foo bar baz quux
+",
+ ))
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn multiple_arches() {
+ assert_eq!(
+ vec![Entry {
+ src: false,
+ arch: Some(vec!["amd64".to_string(), "i386".to_string()]),
+ url: "http://foo/".to_string(),
+ suite_codename: "bar".to_string(),
+ components: vec!["baz".to_string(), "quux".to_string()],
+ untrusted: false,
+ },],
+ read(io::Cursor::new(
+ r"
+deb [arch=amd64,i386] http://foo bar baz quux
",
))
.unwrap()