1use crate::errors::*;
2use crate::pkgs::Pkg;
3use std::io::BufReader;
4use std::io::Read;
5
6pub fn parse_control(control: &str) -> Result<Pkg> {
7 let mut name = None;
8 let mut version = None;
9
10 for line in control.lines() {
11 if let Some(value) = line.strip_prefix("Package: ") {
12 name = Some(value.to_string());
13 }
14
15 if let Some(value) = line.strip_prefix("Version: ") {
16 version = Some(value.to_string());
17 }
18 }
19
20 Ok(Pkg {
21 name: name.context("Failed to find package name in deb control data")?,
22 version: version.context("Failed to find package version in deb control data")?,
23 })
24}
25
26pub fn parse_control_tar<R: Read>(filename: &[u8], reader: R) -> Result<Pkg> {
27 let mut buf = Vec::new();
28 let mut reader = BufReader::new(reader);
29 match filename {
30 b"control.tar.xz" => lzma_rs::xz_decompress(&mut reader, &mut buf)?,
31 _ => bail!("Unsupported compression for control.tar: {filename:?}"),
32 }
33
34 let mut tar = tar::Archive::new(&buf[..]);
35 for entry in tar.entries()? {
36 let mut entry = entry?;
37 let path = entry.path()?;
38 let filename = path
39 .to_str()
40 .with_context(|| anyhow!("Package contains paths with invalid encoding: {:?}", path))?;
41
42 if filename == "./control" {
43 let mut buf = String::new();
44 entry.read_to_string(&mut buf)?;
45 return parse_control(&buf);
46 }
47 }
48
49 bail!("Failed to find control data in control.tar")
50}
51
52pub fn parse<R: Read>(reader: R) -> Result<Pkg> {
53 let mut archive = ar::Archive::new(reader);
54 while let Some(entry) = archive.next_entry() {
55 let mut entry = entry?;
56 let filename = entry.header().identifier();
57 if !filename.starts_with(b"control.tar") {
58 continue;
59 }
60 let filename = filename.to_owned();
61 return parse_control_tar(&filename, &mut entry);
62 }
63
64 bail!("Failed to find control data")
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn test_parse_control_data() -> Result<()> {
73 let data = "Package: binutils-common\nSource: binutils\nVersion: 2.40-2\nArchitecture: amd64\nMaintainer: Matthias Klose <doko@debian.org>\nInstalled-Size: 15021\nBreaks: binutils (<< 2.38.50.20220527-2), binutils-multiarch (<< 2.38.50.20220527-2)\nReplaces: binutils (<< 2.38.50.20220527-2), binutils-multiarch (<< 2.38.50.20220527-2)\nSection: devel\nPriority: optional\nMulti-Arch: same\nHomepage: https://www.gnu.org/software/binutils/\nDescription: Common files for the GNU assembler, linker and binary utilities\n This package contains the localization files used by binutils packages for\n various target architectures and parts of the binutils documentation. It is\n not useful on its own.\n";
74 let data = parse_control(data)?;
75 assert_eq!(
76 data,
77 Pkg {
78 name: "binutils-common".to_string(),
79 version: "2.40-2".to_string(),
80 }
81 );
82 Ok(())
83 }
84
85 #[test]
86 fn test_parse_deb() -> Result<()> {
87 let tar = {
88 let data = b"Package: binutils-common\nSource: binutils\nVersion: 2.40-2\nArchitecture: amd64\nMaintainer: Matthias Klose <doko@debian.org>\nInstalled-Size: 15021\nBreaks: binutils (<< 2.38.50.20220527-2), binutils-multiarch (<< 2.38.50.20220527-2)\nReplaces: binutils (<< 2.38.50.20220527-2), binutils-multiarch (<< 2.38.50.20220527-2)\nSection: devel\nPriority: optional\nMulti-Arch: same\nHomepage: https://www.gnu.org/software/binutils/\nDescription: Common files for the GNU assembler, linker and binary utilities\n This package contains the localization files used by binutils packages for\n various target architectures and parts of the binutils documentation. It is\n not useful on its own.\n";
89
90 let mut tar = tar::Builder::new(Vec::new());
91
92 let mut header = tar::Header::from_byte_slice(&[
94 0x2e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
95 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x36, 0x34, 0x34, 0x00, 0x30, 0x30, 0x30, 0x30,
102 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30,
103 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x37, 0x30, 0x34, 0x00, 0x31, 0x34, 0x34, 0x31,
104 0x34, 0x37, 0x34, 0x35, 0x31, 0x30, 0x34, 0x00, 0x30, 0x31, 0x31, 0x33, 0x32, 0x32,
105 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00, 0x72,
113 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 ])
132 .clone();
133
134 header.set_size(data.len() as u64);
135 header.set_cksum();
136 tar.append(&header, &data[..])?;
137
138 tar.into_inner()?
139 };
140 let compressed = {
141 let mut compressed = Vec::new();
142 lzma_rs::xz_compress(&mut &tar[..], &mut compressed)?;
143 compressed
144 };
145 let deb = {
146 let mut ar = ar::Builder::new(Vec::new());
147 let header = ar::Header::new(b"control.tar.xz".to_vec(), compressed.len() as u64);
148 ar.append(&header, &compressed[..])?;
149 ar.into_inner()?
150 };
151
152 let pkg = parse(&deb[..])?;
153 assert_eq!(
154 pkg,
155 Pkg {
156 name: "binutils-common".to_string(),
157 version: "2.40-2".to_string(),
158 }
159 );
160
161 Ok(())
162 }
163}