openpgp 0.1.5

Low-level OpenPGP packet operations based on rPGP
Documentation
// SPDX-FileCopyrightText: Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::io::{BufRead, BufReader};
use std::path::PathBuf;

use anyhow::anyhow;
use pgp::armor;
use pgp::packet::{Packet, PacketParser};

/// Check if the OpenPGP data in `input` seems to be ASCII-armored or binary (by looking at the
/// highest bit of the first byte)
pub(crate) fn is_binary<R: BufRead>(input: &mut R) -> anyhow::Result<bool> {
    // Peek at the first byte in the reader
    let buf = input.fill_buf()?;
    if buf.is_empty() {
        return Err(anyhow!("empty input"));
    }

    // If the first bit of the first byte is set, we assume this is binary OpenPGP data, otherwise
    // we assume it is ASCII-armored.
    let binary = buf[0] & 0x80 != 0;

    Ok(binary)
}

/// Read OpenPGP packets into an Iterator.
///
/// Reads from the path in `input` - or stdin if `None`.
/// Dearmors if the reader seems to contain armored data.
pub(crate) fn parse_packets<'a>(
    input: Option<PathBuf>,
) -> anyhow::Result<Box<dyn Iterator<Item = pgp::errors::Result<Packet>> + 'a>> {
    let r: Box<dyn std::io::Read> = match input {
        None => Box::new(std::io::stdin()),
        Some(path) => Box::new(std::fs::File::open(path).expect("FIXME")),
    };

    let mut buf = BufReader::new(r);

    // detect armor
    match is_binary(&mut buf)? {
        false => {
            let dearmor = armor::Dearmor::new(buf);
            Ok(Box::new(PacketParser::new(BufReader::new(dearmor))))
        }
        true => Ok(Box::new(PacketParser::new(buf))),
    }
}