Skip to content
24 changes: 15 additions & 9 deletions src/release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct RequestedRelease {
pub codename: String,

pub arches: Vec<String>,
pub untrusted: bool,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -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()]);
Expand Down Expand Up @@ -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");
Expand All @@ -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))?;
Expand Down
10 changes: 8 additions & 2 deletions src/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ impl<'k> GpgClient<'k> {
GpgClient { keyring }
}

pub fn verify_clearsigned<P: AsRef<Path>, Q: AsRef<Path>>(
pub fn read_clearsigned<P: AsRef<Path>, Q: AsRef<Path>>(
&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(
Expand All @@ -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)
Expand Down
100 changes: 98 additions & 2 deletions src/sources_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,38 @@ pub struct Entry {
pub suite_codename: String,
pub components: Vec<String>,
pub arch: Option<String>,
pub untrusted: bool,
}

#[derive(Default)]
struct ParsedOpts {
arch: Option<String>,
untrusted: Option<bool>,
}

fn parse_opts(opts: Option<&str>) -> Result<ParsedOpts, Error> {
let mut ret = ParsedOpts::default();
if let Some(opts) = opts {
if opts.contains(" ") {
bail!("only one option per line supported")
}
let parts: Vec<_> = opts
.strip_prefix("[")
.expect("opening [")
.strip_suffix("]")
.expect("closing ]")
.split("=")
.collect();
match parts.len() {
2 => match parts[0] {
"arch" => ret.arch = Some(parts[1].to_string()),
"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<Vec<Entry>, Error> {
Expand All @@ -33,7 +65,7 @@ fn read_single_line(line: &str) -> Result<Vec<Entry>, Error> {
let src = parts
.next()
.ok_or_else(|| anyhow!("deb{{,s,-src}} section required"))?;
let arch = match parts.peek() {
let opts = match parts.peek() {
Some(&val) if val.starts_with("[") => {
parts.next();
Some(val)
Expand All @@ -60,6 +92,7 @@ fn read_single_line(line: &str) -> Result<Vec<Entry>, Error> {

let mut ret = Vec::with_capacity(srcs.len());

let parsed_opts = parse_opts(opts)?;
for src in srcs {
ret.push(Entry {
src: *src,
Expand All @@ -70,7 +103,8 @@ fn read_single_line(line: &str) -> Result<Vec<Entry>, 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),
});
}

Expand Down Expand Up @@ -113,19 +147,81 @@ 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,
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 http://foo bar baz quux
deb-src http://foo bar baz quux
",
))
.unwrap()
);
}

#[test]
fn arch() {
assert_eq!(
vec![Entry {
src: false,
arch: Some("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()
Expand Down