sequoia_openpgp/packet/
literal.rs

1use std::fmt;
2use std::cmp;
3use std::convert::TryInto;
4use std::time;
5
6#[cfg(test)]
7use quickcheck::{Arbitrary, Gen};
8
9use crate::types::{DataFormat, Timestamp};
10use crate::Error;
11use crate::packet;
12use crate::Packet;
13use crate::Result;
14
15/// Holds a literal packet.
16///
17/// A literal packet contains unstructured data.  Since the size can
18/// be very large, it is advised to process messages containing such
19/// packets using a `PacketParser` or a `PacketPileParser` and process
20/// the data in a streaming manner rather than the using the
21/// `PacketPile::from_file` and related interfaces.
22///
23/// See [Section 5.9 of RFC 9580] for details.
24///
25///   [Section 5.9 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.9
26// IMPORTANT: If you add fields to this struct, you need to explicitly
27// IMPORTANT: implement PartialEq, Eq, and Hash.
28#[derive(Clone, PartialEq, Eq, Hash)]
29pub struct Literal {
30    /// CTB packet header fields.
31    pub(crate) common: packet::Common,
32    /// A one-octet field that describes how the data is formatted.
33    format: DataFormat,
34    /// filename is a string, but strings in Rust are valid UTF-8.
35    /// There is no guarantee, however, that the filename is valid
36    /// UTF-8.  Thus, we leave filename as a byte array.  It can be
37    /// converted to a string using String::from_utf8() or
38    /// String::from_utf8_lossy().
39    filename: Option<Vec<u8>>,
40    /// A four-octet number that indicates a date associated with the
41    /// literal data.
42    date: Option<Timestamp>,
43    /// The literal data packet is a container packet, but cannot
44    /// store packets.
45    ///
46    /// This is written when serialized, and set by the packet parser
47    /// if `buffer_unread_content` is used.
48    container: packet::Container,
49}
50assert_send_and_sync!(Literal);
51
52impl fmt::Debug for Literal {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        let filename = self
55            .filename
56            .as_ref()
57            .map(|filename| String::from_utf8_lossy(filename));
58
59        let threshold = 36;
60        let body = self.body();
61        let prefix = &body[..cmp::min(threshold, body.len())];
62        let mut prefix_fmt = String::from_utf8_lossy(prefix).into_owned();
63        if body.len() > threshold {
64            prefix_fmt.push_str("...");
65        }
66        prefix_fmt.push_str(&format!(" ({} bytes)", body.len())[..]);
67
68        f.debug_struct("Literal")
69            .field("format", &self.format)
70            .field("filename", &filename)
71            .field("date", &self.date)
72            .field("body", &prefix_fmt)
73            .field("body_digest", &self.container.body_digest())
74            .finish()
75    }
76}
77
78impl Default for Literal {
79    fn default() -> Self {
80        Self::new(Default::default())
81    }
82}
83
84impl Literal {
85    /// Returns a new `Literal` packet.
86    pub fn new(format: DataFormat) -> Literal {
87        Literal {
88            common: Default::default(),
89            format,
90            filename: None,
91            date: None,
92            container: packet::Container::default_unprocessed(),
93        }
94    }
95
96    /// Gets the Literal packet's content disposition.
97    pub fn format(&self) -> DataFormat {
98        self.format
99    }
100
101    /// Sets the Literal packet's content disposition.
102    pub fn set_format(&mut self, format: DataFormat) -> DataFormat {
103        ::std::mem::replace(&mut self.format, format)
104    }
105
106    /// Gets the literal packet's filename.
107    ///
108    /// Note: when a literal data packet is protected by a signature,
109    /// only the literal data packet's body is protected, not the
110    /// meta-data.  As such, this field should normally be ignored.
111    pub fn filename(&self) -> Option<&[u8]> {
112        self.filename.as_deref()
113    }
114
115    /// Sets the literal packet's filename field.
116    ///
117    /// The standard does not specify the encoding.  Filenames must
118    /// not be longer than 255 bytes.
119    ///
120    /// Note: when a literal data packet is protected by a signature,
121    /// only the literal data packet's body is protected, not the
122    /// meta-data.  As such, this field should not be used.
123    pub fn set_filename<F>(&mut self, filename: F)
124                           -> Result<Option<Vec<u8>>>
125        where F: AsRef<[u8]>
126    {
127        let filename = filename.as_ref();
128        Ok(::std::mem::replace(&mut self.filename, match filename.len() {
129            0 => None,
130            1..=255 => Some(filename.to_vec()),
131            n => return
132                Err(Error::InvalidArgument(
133                    format!("filename too long: {} bytes", n)).into()),
134        }))
135    }
136
137    /// Gets the literal packet's date field.
138    ///
139    /// Note: when a literal data packet is protected by a signature,
140    /// only the literal data packet's body is protected, not the
141    /// meta-data.  As such, this field should normally be ignored.
142    pub fn date(&self) -> Option<time::SystemTime> {
143        self.date.map(|d| d.into())
144    }
145
146    /// Sets the literal packet's date field.
147    ///
148    /// Note: when a literal data packet is protected by a signature,
149    /// only the literal data packet's body is protected, not the
150    /// meta-data.  As such, this field should not be used.
151    pub fn set_date<T>(&mut self, timestamp: T)
152                       -> Result<Option<time::SystemTime>>
153        where T: Into<Option<time::SystemTime>>
154    {
155        let date = if let Some(d) = timestamp.into() {
156            let t = d.try_into()?;
157            if u32::from(t) == 0 {
158                None // RFC4880, section 5.9: 0 =^= "no specific time".
159            } else {
160                Some(t)
161            }
162        } else {
163            None
164        };
165        Ok(std::mem::replace(&mut self.date, date).map(|d| d.into()))
166    }
167}
168
169impl_unprocessed_body_forwards!(Literal);
170
171impl From<Literal> for Packet {
172    fn from(s: Literal) -> Self {
173        Packet::Literal(s)
174    }
175}
176
177#[cfg(test)]
178impl Arbitrary for Literal {
179    fn arbitrary(g: &mut Gen) -> Self {
180        let mut l = Literal::new(DataFormat::arbitrary(g));
181        l.set_body(Vec::<u8>::arbitrary(g));
182        while let Err(_) = l.set_filename(&Vec::<u8>::arbitrary(g)) {
183            // Too long, try again.
184        }
185        l.set_date(Some(Timestamp::arbitrary(g).into())).unwrap();
186        l
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193    use crate::parse::Parse;
194    use crate::serialize::MarshalInto;
195
196    quickcheck! {
197        fn roundtrip(p: Literal) -> bool {
198            let q = Literal::from_bytes(&p.to_vec().unwrap()).unwrap();
199            assert_eq!(p, q);
200            true
201        }
202    }
203
204    /// Checks that partially read packets are still considered equal.
205    #[test]
206    fn partial_read_eq() -> Result<()> {
207        use buffered_reader::BufferedReader;
208        use crate::parse::PacketParserBuilder;
209
210        let mut l0 = Literal::new(Default::default());
211        l0.set_body(vec![0, 0]);
212        let l0 = Packet::from(l0);
213        let l0bin = l0.to_vec()?;
214        // Sanity check.
215        assert_eq!(l0, Packet::from_bytes(&l0bin)?);
216
217        for &buffer_unread_content in &[false, true] {
218            for read_n in 0..3 {
219                eprintln!("buffer_unread_content: {:?}, read_n: {}",
220                          buffer_unread_content, read_n);
221
222                let mut b = PacketParserBuilder::from_bytes(&l0bin)?;
223                if buffer_unread_content {
224                    b = b.buffer_unread_content();
225                }
226                let mut pp = b.build()?.unwrap();
227                let d = pp.steal(read_n)?;
228                d.into_iter().for_each(|b| assert_eq!(b, 0));
229                let l = pp.finish()?;
230                assert_eq!(&l0, l);
231                let l = pp.next()?.0;
232                assert_eq!(l0, l);
233            }
234        }
235        Ok(())
236    }
237}