deb_rust/
binary.rs

1/*
2    deb-rust - Rust library for building and reading Deb packages
3    Copyright (C) 2023  NotSludgeBomb
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19//! Build and read binary Deb packages.
20//!
21//! Binary packages contain executable programs, documentation for said executables,
22//! configuration files, libraries, etc. Basically, anything that's not source code.
23//!
24//! # Example
25//!
26//! ```
27//! use std::fs::File;
28//! use deb_rust::*;
29//! use deb_rust::binary::*;
30//!
31//! fn main() -> std::io::Result<()> {
32//!     let mut package = DebPackage::new("example");
33//!
34//!     package = package
35//!         .set_version("0.1.0")
36//!         .set_description("deb-rust example")
37//!         .set_architecture(DebArchitecture::Amd64)
38//!         .with_depend("bash")
39//!         .with_file(DebFile::from_path(
40//!             "target/release/example",
41//!             "/usr/bin/example",
42//!         )?);
43//!
44//!     package.build()?.write(File::create("example.deb")?)?;
45//!
46//!     Ok(())
47//! }
48//! ```
49
50use crate::shared::*;
51
52use std::borrow::Cow;
53use std::fs;
54use std::io::{Error, ErrorKind, Read, Write};
55use std::path::{Path, PathBuf};
56
57use regex::Regex;
58use xz::read::XzDecoder;
59use xz::write::XzEncoder;
60
61// Used in DebPackage to store a package's metadata
62// More about these fields here:
63// https://www.debian.org/doc/debian-policy/ch-controlfields.html#binary-package-control-files-debian-control
64#[derive(Debug)]
65struct DebControl {
66    name: String,
67    version: String,
68    priority: DebPriority,
69    architecture: DebArchitecture,
70    essential: bool,
71    depends: Vec<String>,
72    pre_depends: Vec<String>,
73    recommends: Vec<String>,
74    suggests: Vec<String>,
75    breaks: Vec<String>,
76    conflicts: Vec<String>,
77    provides: Vec<String>,
78    replaces: Vec<String>,
79    enhances: Vec<String>,
80    maintainer: String,
81    description: String,
82    homepage: String,
83    built_using: Vec<[String; 2]>,
84}
85
86impl DebControl {
87    // Converts DebControl into a dpkg-readable control file
88    fn serialize(&self) -> Vec<u8> {
89        let mut write_out = String::new();
90        // Binding temporary values to longer living variables
91        let depends = self.depends.join(", ");
92        let pre_depends = self.pre_depends.join(", ");
93        let recommends = self.recommends.join(", ");
94        let suggests = self.suggests.join(", ");
95        let breaks = self.breaks.join(", ");
96        let conflicts = self.conflicts.join(", ");
97        let provides = self.provides.join(", ");
98        let enhances = self.enhances.join(", ");
99        let built_using = {
100            let mut output: Vec<String> = Vec::new();
101            for build_depend in &self.built_using {
102                output.push(format!("{} (= {})", build_depend[0], build_depend[1]));
103            }
104            output.join(", ")
105        };
106        let control = vec![
107            ["Package", self.name.as_str()],
108            ["Version", self.version.as_str()],
109            ["Priority", self.priority.as_str()],
110            ["Architecture", self.architecture.as_str()],
111            [
112                "Essential",
113                match self.essential {
114                    true => "yes",
115                    false => "no",
116                },
117            ],
118            ["Depends", depends.as_str()],
119            ["Pre-Depends", pre_depends.as_str()],
120            ["Recommends", recommends.as_str()],
121            ["Suggests", suggests.as_str()],
122            ["Breaks", breaks.as_str()],
123            ["Conflicts", conflicts.as_str()],
124            ["Provides", provides.as_str()],
125            ["Enhances", enhances.as_str()],
126            ["Maintainer", self.maintainer.as_str()],
127            ["Description", self.description.as_str()],
128            ["Homepage", self.homepage.as_str()],
129            ["Built-Using", built_using.as_str()],
130        ];
131        for field in control {
132            if !field[1].is_empty() {
133                write_out = format!("{}{}: {}\n", write_out, field[0], field[1]);
134            }
135        }
136        write_out.into_bytes()
137    }
138
139    // Converts a dpkg-readable control file into DebControl
140    fn deserialize(control: Vec<u8>) -> std::io::Result<Self> {
141        // Converts comma-separated lists to Vec<String>
142        fn split_to_vec(input: &str) -> Vec<String> {
143            input
144                .split(',')
145                .map(|str| str.trim().to_string())
146                .collect::<Vec<String>>()
147        }
148
149        let mut output = Self {
150            name: String::new(),
151            version: String::new(),
152            priority: DebPriority::Optional,
153            architecture: DebArchitecture::All,
154            essential: false,
155            depends: Vec::new(),
156            pre_depends: Vec::new(),
157            recommends: Vec::new(),
158            suggests: Vec::new(),
159            breaks: Vec::new(),
160            conflicts: Vec::new(),
161            provides: Vec::new(),
162            replaces: Vec::new(),
163            enhances: Vec::new(),
164            maintainer: String::new(),
165            description: String::new(),
166            homepage: String::new(),
167            built_using: Vec::new(),
168        };
169
170        let mut control_string = match String::from_utf8(control) {
171            Ok(string) => string,
172            Err(e) => return Err(Error::new(ErrorKind::Other, e)),
173        };
174
175        // Splits control into Vec<&str> by line, and then splits
176        // each line into Vec<&str> by the key-value separating colon,
177        // ultimately resulting in Vec<Vec<&str>>
178        let iterator = control_string
179            .split('\n')
180            .collect::<Vec<&str>>()
181            .into_iter()
182            .map(|str| {
183                str.split(':')
184                    .collect::<Vec<&str>>()
185                    .into_iter()
186                    .map(|str| str.trim())
187                    .collect::<Vec<&str>>()
188            })
189            .collect::<Vec<Vec<&str>>>();
190
191        for line in iterator {
192            // This is to ensure that the trailing newline at a control file's
193            // end doesn't cause an error
194            if line.len() == 1 {
195                continue;
196            } else if line.len() != 2 {
197                return Err(Error::new(ErrorKind::Other, "control file is invalid"));
198            }
199            // Matches the key and writes the value to the appropriate field
200            match line[0] {
201                "Package" => {
202                    output.name = line[1].to_string();
203                }
204                "Version" => {
205                    output.version = line[1].to_string();
206                }
207                "Priority" => {
208                    output.priority = DebPriority::from(line[1])?;
209                }
210                "Architecture" => {
211                    output.architecture = DebArchitecture::from(line[1])?;
212                }
213                "Essential" => {
214                    output.essential = match line[1] {
215                        "yes" => true,
216                        "no" => false,
217                        &_ => {
218                            return Err(Error::new(ErrorKind::Other, "control file is invalid"));
219                        }
220                    }
221                }
222                "Depends" => {
223                    output.depends = split_to_vec(line[1]);
224                }
225                "Pre-Depends" => {
226                    output.pre_depends = split_to_vec(line[1]);
227                }
228                "Recommends" => {
229                    output.recommends = split_to_vec(line[1]);
230                }
231                "Suggests" => {
232                    output.suggests = split_to_vec(line[1]);
233                }
234                "Breaks" => {
235                    output.breaks = split_to_vec(line[1]);
236                }
237                "Conflicts" => {
238                    output.conflicts = split_to_vec(line[1]);
239                }
240                "Provides" => {
241                    output.provides = split_to_vec(line[1]);
242                }
243                "Replaces" => {
244                    output.replaces = split_to_vec(line[1]);
245                }
246                "Enhances" => {
247                    output.enhances = split_to_vec(line[1]);
248                }
249                "Maintainer" => {
250                    output.maintainer = line[1].to_string();
251                }
252                "Description" => {
253                    output.description = line[1].to_string();
254                }
255                "Homepage" => {
256                    output.homepage = line[1].to_string();
257                }
258                "Built-Using" => {
259                    // Pulls the version number out of the `name (= ver)` format
260                    // in Built-Using
261                    // god i hate regex syntax
262                    let ver_regex: Regex = Regex::new(r"\(= ([^()]*)\)$").unwrap();
263                    let mut built_using: Vec<[String; 2]> = Vec::new();
264                    let source = split_to_vec(line[1]);
265                    for entry in source {
266                        built_using.push([
267                            entry.split(' ').collect::<Vec<&str>>()[0].to_string(),
268                            match ver_regex.find(line[1]) {
269                                Some(mat) => mat.as_str().to_string(),
270                                None => {
271                                    return Err(Error::new(
272                                        ErrorKind::Other,
273                                        "control file is invalid",
274                                    ));
275                                }
276                            },
277                        ]);
278                    }
279                }
280                &_ => {
281                    return Err(Error::new(ErrorKind::Other, "control file is invalid"));
282                }
283            }
284        }
285
286        Ok(output)
287    }
288}
289
290/// A high-level structure representing a Deb package.
291///
292/// For binary package's, it may be helpful to read
293/// [Debian's documentation on binary packages' metadata][1].
294///
295/// As well, you can read Debian's definition for the package's
296/// [maintainer scripts][2].
297///
298/// [1]: https://www.debian.org/doc/debian-policy/ch-controlfields.html#binary-package-control-files-debian-control
299/// [2]: https://www.debian.org/doc/debian-policy/ch-binary.html#maintainer-scripts
300#[derive(Debug)]
301pub struct DebPackage {
302    control: DebControl,         // Package's metadata
303    data: Vec<DebFile>,          // Package's contents
304    config: Option<DebFile>,     // Package's config script
305    preinst: Option<DebFile>,    // Package's preinstall script
306    postinst: Option<DebFile>,   // Package's postinstall script
307    prerm: Option<DebFile>,      // Package's preuninstall script
308    postrm: Option<DebFile>,     // Package's postuninstall script
309    compression: DebCompression, // Configures the package's compression standard
310}
311
312impl DebPackage {
313    /// Creates a new DebPackage with `name` as it's name.
314    pub fn new(name: &str) -> Self {
315        Self {
316            control: DebControl {
317                name: name.to_string(),
318                version: String::new(),
319                priority: DebPriority::Optional,
320                architecture: DebArchitecture::All,
321                essential: false,
322                depends: Vec::new(),
323                pre_depends: Vec::new(),
324                recommends: Vec::new(),
325                suggests: Vec::new(),
326                breaks: Vec::new(),
327                conflicts: Vec::new(),
328                provides: Vec::new(),
329                replaces: Vec::new(),
330                enhances: Vec::new(),
331                maintainer: String::new(),
332                description: String::new(),
333                homepage: String::new(),
334                built_using: Vec::new(),
335            },
336            data: Vec::new(),
337            config: None,
338            preinst: None,
339            postinst: None,
340            prerm: None,
341            postrm: None,
342            compression: DebCompression::Zstd,
343        }
344    }
345
346    /// Reads a DebPackage from `input`.
347    pub fn from<R: Read>(mut input: R) -> std::io::Result<Self> {
348        DebArchive::read(input)?.to_package()
349    }
350
351    /// Sets the package's name.
352    pub fn set_name(mut self, name: &str) -> Self {
353        self.control.name = name.to_string();
354        self
355    }
356
357    /// Sets the package's version.
358    pub fn set_version(mut self, version: &str) -> Self {
359        self.control.version = version.to_string();
360        self
361    }
362
363    /// Sets the package's priority.
364    pub fn set_priority(mut self, priority: DebPriority) -> Self {
365        self.control.priority = priority;
366        self
367    }
368
369    /// Sets the package's architecture.
370    pub fn set_architecture(mut self, architecture: DebArchitecture) -> Self {
371        self.control.architecture = architecture;
372        self
373    }
374
375    /// Sets whether the package is essential.
376    pub fn set_essential(mut self, essential: bool) -> Self {
377        self.control.essential = essential;
378        self
379    }
380
381    /// Adds a single dependency from &str.
382    pub fn with_depend(mut self, depend: &str) -> Self {
383        self.control.depends.push(depend.to_string());
384        self
385    }
386
387    /// Adds a number of dependencies from Vec<&str>.
388    pub fn with_depends(mut self, depends: Vec<&str>) -> Self {
389        self.control
390            .depends
391            .append(&mut depends.iter().map(|str| str.to_string()).collect());
392        self
393    }
394
395    /// Resets dependencies.
396    pub fn no_depends(mut self) -> Self {
397        self.control.depends = Vec::new();
398        self
399    }
400
401    /// Adds a single pre-dependency from &str.
402    pub fn with_pre_depend(mut self, depend: &str) -> Self {
403        self.control.pre_depends.push(depend.to_string());
404        self
405    }
406
407    /// Adds a number of pre-dependencies from Vec<&str>.
408    pub fn with_pre_depends(mut self, depends: Vec<&str>) -> Self {
409        self.control
410            .pre_depends
411            .append(&mut depends.iter().map(|str| str.to_string()).collect());
412        self
413    }
414
415    /// Resets pre-dependencies.
416    pub fn no_pre_depends(mut self) -> Self {
417        self.control.pre_depends = Vec::new();
418        self
419    }
420
421    /// Adds a single recommend from &str.
422    pub fn with_recommend(mut self, recommend: &str) -> Self {
423        self.control.recommends.push(recommend.to_string());
424        self
425    }
426
427    /// Adds a number of recommends from Vec<&str>.
428    pub fn with_recommends(mut self, recommends: Vec<&str>) -> Self {
429        self.control
430            .recommends
431            .append(&mut recommends.iter().map(|str| str.to_string()).collect());
432        self
433    }
434
435    /// Resets recommends.
436    pub fn no_recommends(mut self) -> Self {
437        self.control.recommends = Vec::new();
438        self
439    }
440
441    /// Adds a single suggest from &str.
442    pub fn with_suggest(mut self, suggest: &str) -> Self {
443        self.control.suggests.push(suggest.to_string());
444        self
445    }
446
447    /// Adds a number of suggests from Vec<&str>.
448    pub fn with_suggests(mut self, suggests: Vec<&str>) -> Self {
449        self.control
450            .suggests
451            .append(&mut suggests.iter().map(|str| str.to_string()).collect());
452        self
453    }
454
455    /// Resets suggests.
456    pub fn no_suggests(mut self) -> Self {
457        self.control.suggests = Vec::new();
458        self
459    }
460
461    /// Adds a single break from &str.
462    pub fn with_break(mut self, conflict: &str) -> Self {
463        self.control.breaks.push(conflict.to_string());
464        self
465    }
466
467    /// Adds a number of breaks from Vec<&str>.
468    pub fn with_breaks(mut self, conflicts: Vec<&str>) -> Self {
469        self.control
470            .breaks
471            .append(&mut conflicts.iter().map(|str| str.to_string()).collect());
472        self
473    }
474
475    /// Resets breaks.
476    pub fn no_breaks(mut self) -> Self {
477        self.control.breaks = Vec::new();
478        self
479    }
480
481    /// Adds a single conflict from &str.
482    pub fn with_conflict(mut self, conflict: &str) -> Self {
483        self.control.conflicts.push(conflict.to_string());
484        self
485    }
486
487    /// Adds a number of conflicts from Vec<&str>.
488    pub fn with_conflicts(mut self, conflicts: Vec<&str>) -> Self {
489        self.control
490            .conflicts
491            .append(&mut conflicts.iter().map(|str| str.to_string()).collect());
492        self
493    }
494
495    /// Resets conflicts.
496    pub fn no_conflicts(mut self) -> Self {
497        self.control.conflicts = Vec::new();
498        self
499    }
500
501    /// Adds a single provide from &str.
502    pub fn with_provide(mut self, provide: &str) -> Self {
503        self.control.provides.push(provide.to_string());
504        self
505    }
506
507    /// Adds a number of provides from Vec<&str>.
508    pub fn with_provides(mut self, provides: Vec<&str>) -> Self {
509        self.control
510            .provides
511            .append(&mut provides.iter().map(|str| str.to_string()).collect());
512        self
513    }
514
515    /// Resets provides.
516    pub fn no_provides(mut self) -> Self {
517        self.control.provides = Vec::new();
518        self
519    }
520
521    /// Adds a single replace from &str.
522    pub fn with_replace(mut self, replace: &str) -> Self {
523        self.control.replaces.push(replace.to_string());
524        self
525    }
526
527    /// Adds a number of replaces from Vec<&str>.
528    pub fn with_replaces(mut self, replaces: Vec<&str>) -> Self {
529        self.control
530            .replaces
531            .append(&mut replaces.iter().map(|str| str.to_string()).collect());
532        self
533    }
534
535    /// Resets replaces.
536    pub fn no_replaces(mut self) -> Self {
537        self.control.replaces = Vec::new();
538        self
539    }
540
541    /// Adds a single enhance from &str.
542    pub fn with_enhance(mut self, enhance: &str) -> Self {
543        self.control.enhances.push(enhance.to_string());
544        self
545    }
546
547    /// Adds a number of enhances from Vec<&str>.
548    pub fn with_enhances(mut self, enhances: Vec<&str>) -> Self {
549        self.control
550            .enhances
551            .append(&mut enhances.iter().map(|str| str.to_string()).collect());
552        self
553    }
554
555    /// Resets enhances.
556    pub fn no_enhances(mut self) -> Self {
557        self.control.enhances = Vec::new();
558        self
559    }
560
561    /// Sets the package's maintainer.
562    pub fn set_maintainer(mut self, maintainer: &str) -> Self {
563        self.control.maintainer = maintainer.to_string();
564        self
565    }
566
567    /// Sets the package's description.
568    pub fn set_description(mut self, description: &str) -> Self {
569        self.control.description = description.to_string();
570        self
571    }
572
573    /// Sets the package's homepage.
574    pub fn set_homepage(mut self, homepage: &str) -> Self {
575        self.control.homepage = homepage.to_string();
576        self
577    }
578
579    /// Adds a "built using" package.
580    pub fn with_built_using(mut self, using: &str, version: &str) -> Self {
581        self.control
582            .built_using
583            .push([using.to_string(), version.to_string()]);
584        self
585    }
586
587    /// Resets built-using.
588    pub fn no_built_using(mut self) -> Self {
589        self.control.built_using = Vec::new();
590        self
591    }
592
593    /// Adds a file to the package.
594    pub fn with_file(mut self, file: DebFile) -> Self {
595        self.data.push(file);
596        self
597    }
598
599    /// Recursively adds directory `from` to package as `to`.
600    ///
601    /// This adds all files and sub-directories to `to`. For example, if you
602    /// had a directory `test` containing the files `foo` and `bar`, then
603    /// you can add those files as `/usr/bin/foo` and `/usr/bin/bar` with
604    /// `with_dir("test", "/usr/bin")?;`
605    ///
606    /// This function isn't available when compiling on Windows, as it's utility
607    /// relies on being able to read the modes of the directory's children,
608    /// which is a feature Windows lacks.
609    ///
610    /// # Errors
611    ///
612    /// This function may return an error if `from` doesn't exist.
613    ///
614    /// # Example
615    ///
616    /// ```
617    /// use deb_rust::binary::DebPackage;
618    ///
619    /// let mut package = DebPackage::new("example")
620    ///     .with_dir("test", "/usr/bin").unwrap();
621    /// ```
622    #[cfg(unix)]
623    pub fn with_dir<P>(mut self, from: P, to: P) -> std::io::Result<Self>
624    where
625        P: AsRef<Path>,
626    {
627        let mut path_from = PathBuf::new();
628        let mut path_to = PathBuf::new();
629        path_from.push(from);
630        path_to.push(to);
631        for file_result in walkdir::WalkDir::new(&path_from) {
632            let file = file_result?;
633            if file.path().is_file() {
634                // Cutting the `from` directory out of the path
635                let mut components = file.path().components();
636                for _i in path_from.components() {
637                    components.next();
638                }
639                self = self.with_file(DebFile::from_path(file.path(), path_to.join(components))?);
640            }
641        }
642        Ok(self)
643    }
644
645    /// Removes all file's from the package.
646    pub fn clear_files(mut self) -> Self {
647        self.data = Vec::new();
648        self
649    }
650
651    /// Sets config script from &str.
652    pub fn config_from_str(mut self, script: &str) -> Self {
653        self.config = Some(DebFile::from_buf(script.as_bytes().to_vec(), "config").is_exec());
654        self
655    }
656
657    /// Sets config script from Vec<u8>.
658    pub fn config_from_buf(mut self, script: Vec<u8>) -> Self {
659        self.config = Some(DebFile::from_buf(script, "config").is_exec());
660        self
661    }
662
663    /// Resets config script.
664    pub fn no_config(mut self) -> Self {
665        self.config = None;
666        self
667    }
668
669    /// Sets preinst script from &str.
670    pub fn preinst_from_str(mut self, script: &str) -> Self {
671        self.preinst = Some(DebFile::from_buf(script.as_bytes().to_vec(), "preinst").is_exec());
672        self
673    }
674
675    /// Sets preinst script from Vec<u8>.
676    pub fn preinst_from_buf(mut self, script: Vec<u8>) -> Self {
677        self.preinst = Some(DebFile::from_buf(script, "preinst").is_exec());
678        self
679    }
680
681    /// Resets preinst script.
682    pub fn no_preinst(mut self) -> Self {
683        self.preinst = None;
684        self
685    }
686
687    /// Sets postinst script from &str.
688    pub fn postinst_from_str(mut self, script: &str) -> Self {
689        self.postinst = Some(DebFile::from_buf(script.as_bytes().to_vec(), "postinst").is_exec());
690        self
691    }
692
693    /// Sets postinst script from Vec<u8>.
694    pub fn postinst_from_buf(mut self, script: Vec<u8>) -> Self {
695        self.postinst = Some(DebFile::from_buf(script, "postinst").is_exec());
696        self
697    }
698
699    /// Resets postinst script.
700    pub fn no_postinst(mut self) -> Self {
701        self.postinst = None;
702        self
703    }
704
705    /// Sets prerm script from &str.
706    pub fn prerm_from_str(mut self, script: &str) -> Self {
707        self.prerm = Some(DebFile::from_buf(script.as_bytes().to_vec(), "prerm").is_exec());
708        self
709    }
710
711    /// Sets prerm script from Vec<u8>.
712    pub fn prerm_from_buf(mut self, script: Vec<u8>) -> Self {
713        self.prerm = Some(DebFile::from_buf(script, "prerm").is_exec());
714        self
715    }
716
717    /// Resets prerm script.
718    pub fn no_prerm(mut self) -> Self {
719        self.prerm = None;
720        self
721    }
722
723    /// Sets postrm script from &str.
724    pub fn postrm_from_str(mut self, script: &str) -> Self {
725        self.postrm = Some(DebFile::from_buf(script.as_bytes().to_vec(), "postrm").is_exec());
726        self
727    }
728
729    /// Sets postrm script from Vec<u8>.
730    pub fn postrm_from_buf(mut self, script: Vec<u8>) -> Self {
731        self.postrm = Some(DebFile::from_buf(script, "postrm").is_exec());
732        self
733    }
734
735    /// Resets postrm script.
736    pub fn no_postrm(mut self) -> Self {
737        self.postrm = None;
738        self
739    }
740
741    /// Sets the package's compression standard.
742    pub fn set_compression(mut self, compression: DebCompression) -> Self {
743        self.compression = compression;
744        self
745    }
746
747    /// Returns the package's name.
748    pub fn name(&self) -> &str {
749        &self.control.name
750    }
751
752    /// Returns the package's version.
753    pub fn version(&self) -> &str {
754        &self.control.version
755    }
756
757    /// Returns the package's priority.
758    pub fn priority(&self) -> &DebPriority {
759        &self.control.priority
760    }
761
762    /// Returns the package's architecture.
763    pub fn architecture(&self) -> &DebArchitecture {
764        &self.control.architecture
765    }
766
767    /// Returns whether the package is essential.
768    pub fn essential(&self) -> bool {
769        self.control.essential
770    }
771
772    /// Returns the package's depends.
773    pub fn depends(&self) -> &Vec<String> {
774        &self.control.depends
775    }
776
777    /// Returns the package's pre-depends.
778    pub fn pre_depends(&self) -> &Vec<String> {
779        &self.control.pre_depends
780    }
781
782    /// Returns the package's recommends.
783    pub fn recommends(&self) -> &Vec<String> {
784        &self.control.recommends
785    }
786
787    /// Returns the package's suggests.
788    pub fn suggests(&self) -> &Vec<String> {
789        &self.control.suggests
790    }
791
792    /// Returns the package's breaks.
793    pub fn breaks(&self) -> &Vec<String> {
794        &self.control.breaks
795    }
796
797    /// Returns the package's conflicts.
798    pub fn conflicts(&self) -> &Vec<String> {
799        &self.control.conflicts
800    }
801
802    /// Returns the package's provides.
803    pub fn provides(&self) -> &Vec<String> {
804        &self.control.provides
805    }
806
807    /// Returns the package's replaces.
808    pub fn replaces(&self) -> &Vec<String> {
809        &self.control.replaces
810    }
811
812    /// Returns the package's enhances.
813    pub fn enhances(&self) -> &Vec<String> {
814        &self.control.enhances
815    }
816
817    /// Returns the package's maintainer.
818    pub fn maintainer(&self) -> &str {
819        &self.control.maintainer
820    }
821
822    /// Returns the package's description.
823    pub fn description(&self) -> &str {
824        &self.control.description
825    }
826
827    /// Returns the package's homepage.
828    pub fn homepage(&self) -> &str {
829        &self.control.homepage
830    }
831
832    /// Returns the packages this package was built with.
833    pub fn built_using(&self) -> &Vec<[String; 2]> {
834        &self.control.built_using
835    }
836
837    /// Returns a vector of the packages files.
838    pub fn files(&self) -> &Vec<DebFile> {
839        &self.data
840    }
841
842    /// Returns the package's config script.
843    pub fn config(&self) -> Option<&Vec<u8>> {
844        match &self.config {
845            Some(file) => Some(file.contents()),
846            None => None,
847        }
848    }
849
850    /// Returns the package's preinst script.
851    pub fn preinst(&self) -> Option<&Vec<u8>> {
852        match &self.preinst {
853            Some(file) => Some(file.contents()),
854            None => None,
855        }
856    }
857
858    /// Returns the package's postinst script.
859    pub fn postinst(&self) -> Option<&Vec<u8>> {
860        match &self.postinst {
861            Some(file) => Some(file.contents()),
862            None => None,
863        }
864    }
865
866    /// Returns the package's prerm script.
867    pub fn prerm(&self) -> Option<&Vec<u8>> {
868        match &self.prerm {
869            Some(file) => Some(file.contents()),
870            None => None,
871        }
872    }
873
874    /// Returns the package's postrm script.
875    pub fn postrm(&self) -> Option<&Vec<u8>> {
876        match &self.postrm {
877            Some(file) => Some(file.contents()),
878            None => None,
879        }
880    }
881
882    /// Returns the package's compression standard.
883    pub fn compression(&self) -> &DebCompression {
884        &self.compression
885    }
886
887    /// Builds the package into a DebArchive struct.
888    pub fn build(&self) -> std::io::Result<DebArchive> {
889        let mut output = DebArchive {
890            control: Vec::new(),
891            data: Vec::new(),
892            compression: match self.compression {
893                DebCompression::Xz => DebCompression::Xz,
894                DebCompression::Zstd => DebCompression::Zstd,
895            },
896        };
897
898        // Creating tar archives
899        let mut control_tar = tar::Builder::new(Vec::new());
900        let mut data_tar = tar::Builder::new(Vec::new());
901
902        // Creating DebFile's from control and scripts
903        let control_file = Some(DebFile::from_buf(self.control.serialize(), "control"));
904        let mut control_vec = vec![
905            &control_file,
906            &self.config,
907            &self.preinst,
908            &self.postinst,
909            &self.prerm,
910            &self.postrm,
911        ];
912
913        // Adding files to control tar
914        for file in control_vec.into_iter().flatten() {
915            let mut file_header = tar::Header::new_gnu();
916            // We don't have to worry about the path being absolute here as all
917            // scripts can only have relative paths using the struct's methods
918            file_header.set_path(file.path())?;
919            file_header.set_size(file.contents().len().try_into().unwrap());
920            file_header.set_mode(*file.mode());
921            file_header.set_cksum();
922            control_tar.append(&file_header, file.contents().as_slice())?;
923        }
924
925        // Adding files to data tar
926        for file in &self.data {
927            let mut file_header = tar::Header::new_gnu();
928            // We have to strip the root directory if the path is absolute
929            // as the tar library doesn't allow absolute paths
930            if file.path().is_absolute() {
931                match file.path().strip_prefix("/") {
932                    Ok(path) => {
933                        file_header.set_path(path)?;
934                    }
935                    Err(e) => {
936                        return Err(Error::new(ErrorKind::Other, e));
937                    }
938                }
939            } else {
940                file_header.set_path(file.path())?;
941            }
942            file_header.set_size(file.contents().len().try_into().unwrap());
943            file_header.set_mode(*file.mode());
944            file_header.set_cksum();
945            data_tar.append(&file_header, file.contents().as_slice())?;
946        }
947
948        // Compressing tar archives to DebArchive struct
949        match self.compression {
950            DebCompression::Xz => {
951                let mut control_xz = XzEncoder::new(&mut output.control, 9);
952                control_xz.write_all(control_tar.into_inner()?.as_slice())?;
953                control_xz.finish()?;
954                let mut data_xz = XzEncoder::new(&mut output.data, 9);
955                data_xz.write_all(data_tar.into_inner()?.as_slice())?;
956                data_xz.finish()?;
957            }
958            DebCompression::Zstd => {
959                zstd::stream::copy_encode(
960                    control_tar.into_inner()?.as_slice(),
961                    &mut output.control,
962                    0,
963                )?;
964                zstd::stream::copy_encode(data_tar.into_inner()?.as_slice(), &mut output.data, 0)?;
965            }
966        }
967
968        Ok(output)
969    }
970}
971
972/// An intermediary layer between the DebPackage struct and an actual .deb file.
973///
974/// This struct allows you to read and write built packages from and to the filesystem.
975///
976/// The contents of a DebArchive cannot be directly manipulated. To modify a DebArchive,
977/// you must first convert it to a DebPackage with the `to_package()` method, or open the
978/// file using DebPackage's `from()` function.
979pub struct DebArchive {
980    control: Vec<u8>,            // Compressed tar archive containing package's metadata
981    data: Vec<u8>,               // Compressed tar archive containing the package's contents
982    compression: DebCompression, // Configuration for the package's compression standard
983}
984
985impl DebArchive {
986    /// Writes package to `output`.
987    pub fn write<W: Write>(&self, mut output: W) -> std::io::Result<()> {
988        // Parsing the name of the control and data archives
989        let (control_name, data_name) = match self.compression {
990            DebCompression::Xz => ("control.tar.xz", "data.tar.xz"),
991            DebCompression::Zstd => ("control.tar.zst", "data.tar.zst"),
992        };
993
994        // Creating final archive
995        let mut archive = ar::Builder::new(Vec::new());
996
997        // Frankly not quite sure what the "debian-binary" file is for,
998        // but it just contains the text "2.0"
999        let mut header = ar::Header::new("debian-binary".as_bytes().to_vec(), 4);
1000        header.set_mode(33188);
1001        archive.append(&header, "2.0\n".as_bytes())?;
1002
1003        // Adding control to archive
1004        let mut header = ar::Header::new(
1005            control_name.as_bytes().to_vec(),
1006            self.control.len().try_into().unwrap(),
1007        );
1008        header.set_mode(33188);
1009        archive.append(&header, self.control.as_slice())?;
1010
1011        // Adding data to archive
1012        let mut header = ar::Header::new(
1013            data_name.as_bytes().to_vec(),
1014            self.data.len().try_into().unwrap(),
1015        );
1016        header.set_mode(33188);
1017        archive.append(&header, self.data.as_slice())?;
1018
1019        // Writing archive to `out`
1020        output.write(&archive.into_inner()?);
1021        Ok(())
1022    }
1023
1024    /// Reads package from `input`.
1025    pub fn read<R: Read>(mut input: R) -> std::io::Result<Self> {
1026        // Preparing output
1027        let mut output = Self {
1028            control: Vec::new(),
1029            data: Vec::new(),
1030            compression: DebCompression::Zstd,
1031        };
1032
1033        // Creating Archive reader and iterator
1034        let mut archive = ar::Archive::new(input);
1035
1036        // Skipping `debian-binary` file
1037        archive.next_entry().unwrap()?;
1038
1039        // Reading control archive
1040        match archive.next_entry() {
1041            Some(entry) => {
1042                entry?.read_to_end(&mut output.control)?;
1043            }
1044            None => {
1045                return Err(Error::new(
1046                    ErrorKind::Other,
1047                    "deb package is missing archive",
1048                ));
1049            }
1050        }
1051
1052        // Reading data archive
1053        let mut data_entry = match archive.next_entry() {
1054            Some(entry) => entry?,
1055            None => {
1056                return Err(Error::new(
1057                    ErrorKind::Other,
1058                    "deb package is missing archive",
1059                ))
1060            }
1061        };
1062        data_entry.read_to_end(&mut output.data)?;
1063
1064        // Reading data header to parse compression
1065        let mut data_identifier = String::new();
1066        match String::from_utf8(data_entry.header().identifier().to_vec()) {
1067            Ok(id) => {
1068                data_identifier = id;
1069            }
1070            Err(e) => {
1071                return Err(Error::new(ErrorKind::Other, e));
1072            }
1073        }
1074        if let Some(ext) = Path::new(&data_identifier).extension() {
1075            if ext.to_str() == Some("xz") {
1076                output.compression = DebCompression::Xz;
1077            }
1078        }
1079
1080        Ok(output)
1081    }
1082
1083    /// Converts DebArchive to DebPackage.
1084    ///
1085    /// # Errors
1086    ///
1087    /// This function may return an error if the archive's control file (where all
1088    /// of a package's metadata is stored) contains invalid syntax, or if part of the
1089    /// package is corrupted and can't be read.
1090    pub fn to_package(&self) -> std::io::Result<DebPackage> {
1091        let mut output = DebPackage::new("");
1092        output.compression = match self.compression {
1093            DebCompression::Xz => DebCompression::Xz,
1094            DebCompression::Zstd => DebCompression::Zstd,
1095        };
1096
1097        // Decompressing control and data archives
1098        let mut control_buf: Vec<u8> = Vec::new();
1099        let mut data_buf: Vec<u8> = Vec::new();
1100        match self.compression {
1101            DebCompression::Xz => {
1102                XzDecoder::new(self.control.as_slice()).read_to_end(&mut control_buf)?;
1103                XzDecoder::new(self.data.as_slice()).read_to_end(&mut data_buf)?;
1104            }
1105            DebCompression::Zstd => {
1106                zstd::stream::copy_decode(self.control.as_slice(), &mut control_buf)?;
1107                zstd::stream::copy_decode(self.data.as_slice(), &mut data_buf)?;
1108            }
1109        };
1110        let mut control_tar = tar::Archive::new(control_buf.as_slice());
1111        let mut data_tar = tar::Archive::new(data_buf.as_slice());
1112
1113        // Parsing control archive
1114        for entry_result in control_tar.entries()? {
1115            let mut entry = entry_result?;
1116            let mut buf: Vec<u8> = Vec::new();
1117            entry.read_to_end(&mut buf);
1118            if entry.path()? == Cow::Borrowed(Path::new("control")) {
1119                // Converting control file into DebControl struct
1120                output.control = DebControl::deserialize(buf)?;
1121            } else if entry.path()? == Cow::Borrowed(Path::new("config")) {
1122                output = output.config_from_buf(buf);
1123            } else if entry.path()? == Cow::Borrowed(Path::new("preinst")) {
1124                output = output.preinst_from_buf(buf);
1125            } else if entry.path()? == Cow::Borrowed(Path::new("postinst")) {
1126                output = output.postinst_from_buf(buf);
1127            } else if entry.path()? == Cow::Borrowed(Path::new("prerm")) {
1128                output = output.prerm_from_buf(buf);
1129            } else if entry.path()? == Cow::Borrowed(Path::new("postrm")) {
1130                output = output.postrm_from_buf(buf);
1131            }
1132        }
1133
1134        // Converting data entries to DebFile structs
1135        for entry_result in data_tar.entries()? {
1136            let mut entry = entry_result?;
1137            let mut buf: Vec<u8> = Vec::new();
1138            entry.read_to_end(&mut buf);
1139            if let Cow::Borrowed(path) = entry.path()? {
1140                output.data.push(
1141                    DebFile::from_buf(buf, format!("/{}", path.display()))
1142                        .set_mode(entry.header().mode()?),
1143                )
1144            }
1145        }
1146
1147        Ok(output)
1148    }
1149}