1use std::io::BufRead;
2
3use log::debug;
4
5use crate::{
6 composed::PacketBodyReader,
7 errors::{Error, Result},
8 packet::{Packet, PacketHeader},
9};
10
11pub struct PacketParser<R: BufRead> {
13 reader: R,
15 is_done: bool,
17}
18
19impl<R: BufRead> PacketParser<R> {
20 pub fn new(source: R) -> Self {
21 PacketParser {
22 reader: source,
23 is_done: false,
24 }
25 }
26
27 pub fn into_inner(self) -> R {
28 self.reader
29 }
30}
31
32impl<R: BufRead> Iterator for PacketParser<R> {
33 type Item = Result<Packet>;
34
35 fn next(&mut self) -> Option<Self::Item> {
36 if self.is_done {
37 return None;
38 }
39
40 let header = match PacketHeader::try_from_reader(&mut self.reader) {
41 Ok(header) => header,
42 Err(err) => {
43 self.is_done = true;
44 if err.kind() == std::io::ErrorKind::UnexpectedEof {
45 return None;
46 }
47
48 return Some(Err(err.into()));
49 }
50 };
51
52 debug!("found header: {header:?}");
53 let res = PacketBodyReader::new(header, &mut self.reader)
54 .map_err(Error::from)
55 .and_then(|mut body| {
56 match Packet::from_reader(header, &mut body) {
57 Ok(packet) => Ok(packet),
58 Err(Error::PacketParsing { source }) if source.is_incomplete() => {
59 debug!("incomplete packet for: {source:?}");
60 Err(Error::PacketIncomplete { source })
62 }
63 Err(err) => Err(err),
64 }
65 });
66 Some(res)
67 }
68}
69
70impl<R: BufRead> PacketParser<R> {
71 pub fn next_ref(&mut self) -> Option<Result<PacketBodyReader<&'_ mut R>>> {
72 if self.is_done {
73 return None;
74 }
75
76 let header = match PacketHeader::try_from_reader(&mut self.reader) {
77 Ok(header) => header,
78 Err(err) => {
79 if err.kind() == std::io::ErrorKind::UnexpectedEof {
80 return None;
81 }
82
83 self.is_done = true;
84 return Some(Err(err.into()));
85 }
86 };
87
88 debug!("found header: {header:?}");
89 let body = PacketBodyReader::new(header, &mut self.reader).map_err(Into::into);
90 Some(body)
91 }
92
93 pub fn next_owned(mut self) -> Option<Result<PacketBodyReader<R>>> {
94 if self.is_done {
95 return None;
96 }
97
98 let header = match PacketHeader::try_from_reader(&mut self.reader) {
99 Ok(header) => header,
100 Err(err) => {
101 self.is_done = true;
102 return Some(Err(err.into()));
103 }
104 };
105
106 debug!("found header: {header:?}");
107 let body = PacketBodyReader::new(header, self.reader).map_err(Into::into);
108 Some(body)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use std::{
115 fs::File,
116 io::{BufRead, BufReader, Read, Seek, SeekFrom},
117 path::Path,
118 };
119
120 use log::warn;
121 use regex::Regex;
122
123 use super::*;
124 use crate::{packet::PacketTrait, ser::Serialize, types::Tag};
125
126 #[test]
127 #[ignore]
128 fn test_packet_roundtrip_0001() {
129 packet_roundtrip(
130 "0001",
131 vec![
132 (556, 6 + 2224),
133 (805, 6 + 95),
134 (806, 6 + 6495),
135 (1027, 6 + 6246),
136 (1074, 2490),
137 (1331, 838),
138 (1898, 6420),
139 (1935, 3583),
140 ],
141 )
142 }
143
144 #[test]
145 #[ignore]
146 fn test_packet_roundtrip_0002() {
147 packet_roundtrip(
148 "0002",
149 vec![
150 (82, 199), (85, 196), (836, 3136), (1200, 2772), (1268, 1223), (1670, 3419), ],
157 )
158 }
159
160 #[test]
161 #[ignore]
162 fn test_packet_roundtrip_0009() {
163 packet_roundtrip(
164 "0009",
165 vec![
166 (37, 3960), (39, 3960), (258, 75), (260, 78), (1053, 3181), (1473, 5196), (1895, 4243), ],
174 )
175 }
176
177 fn packet_roundtrip(dump: &str, skips: Vec<(usize, i64)>) {
178 let _ = pretty_env_logger::try_init();
179
180 let path = format!("./tests/tests/sks-dump/{dump}.pgp");
181 let p = Path::new(&path);
182 let mut file = BufReader::new(std::fs::File::open(p).unwrap());
183 let mut bytes = File::open(p).unwrap();
184
185 let packets = PacketParser::new(&mut file);
186
187 for (i, packet) in packets.take(2000).enumerate() {
188 if let Some((_, size)) = skips.iter().find(|(j, _)| *j == i) {
190 bytes.seek(SeekFrom::Current(*size)).unwrap();
191 continue;
192 }
193
194 let packet = packet.expect("invalid packet");
195 let mut buf = Vec::new();
196 packet
197 .to_writer(&mut buf)
198 .expect("failed to serialize packet");
199
200 let mut expected_buf = vec![0u8; buf.len()];
201 assert_eq!(bytes.read(&mut expected_buf).unwrap(), buf.len());
202 if buf != expected_buf {
205 assert_eq!(hex::encode(buf), hex::encode(expected_buf));
206 }
207 }
208 }
209
210 #[test]
211 #[ignore]
212 fn test_many_parser() {
213 let _ = pretty_env_logger::try_init();
214
215 let p = Path::new("./tests/tests/sks-dump/0000.pgp");
216 let file = BufReader::new(std::fs::File::open(p).unwrap());
217
218 let fixture = File::open("./tests/tests/sks-dump/0000_parsed.txt").unwrap();
222 let re = Regex::new(r"^#\soff=(\d+)\sctb=[[:alpha:]\d]+\stag=(\d+)\s.*").unwrap();
223 let expected_tags = BufReader::new(fixture)
224 .lines()
225 .filter(|line| line.as_ref().unwrap().starts_with("# off"))
226 .map(|line| {
227 let (offset, tag) = {
228 let cap = re.captures(line.as_ref().unwrap()).unwrap();
229 (cap[1].to_string(), cap[2].to_string())
230 };
231
232 (offset, tag, line)
233 })
234 .filter(|(offset, _, _)| {
235 let list = [
236 "1193538", "9218758", "6844449", "24798372", "38521947", "32162244", "43825283", "9745167", "9797527", "19045846", "19047047", "3122229", "15412686", "15416968", "19333639", "19336829", "19690793", "19697083", "19703308", "20084489", "20098904", "22420219", "22424421", "23086701", "24016578", "24022877", "41034801", ];
264 if list.contains(&offset.as_str()) {
265 warn!("skipping {offset}");
266 false
267 } else {
268 true
269 }
270 });
271
272 let actual_tags = PacketParser::new(file).filter(|p| {
273 p.as_ref()
274 .inspect_err(|e| {
275 warn!("failed to parse packet: {e:?}");
276 })
277 .is_ok()
278 });
279 let iter = expected_tags.zip(actual_tags);
280
281 for ((_offset, tag, e), packet) in iter {
282 let e = e.as_ref().unwrap();
283 let packet = packet.unwrap();
284
285 println!("-- checking: {:?} {}", packet.tag(), e);
286
287 let tag: Tag = u8::into(tag.parse().unwrap());
288 assert_eq!(tag, packet.tag(), "mismatch in packet {:?} ({})", p, e);
289 }
290 }
291
292 #[test]
293 fn incomplete_packet_parser() {
294 let _ = pretty_env_logger::try_init();
295
296 let bytes = [0x97];
297 let parser = PacketParser::new(&bytes[..]);
298 let mut packets = parser.filter_map(|p| {
299 match p {
301 Ok(pp) => Some(pp),
302 Err(err) => {
303 log::warn!("skipping packet: {err:?}");
304 None
305 }
306 }
307 });
308 assert!(packets.next().is_none());
309 }
310
311 #[test]
312 fn test_partial_length_encoding() {
313 let _ = pretty_env_logger::try_init();
314
315 use crate::composed::Message;
316
317 const TEXT: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n";
318
319 for msg_file in [
320 "./tests/unit-tests/partial-body-length/literal.packet-two-octet-length.asc",
322 "./tests/unit-tests/partial-body-length/literal.packet-partial.512.asc",
324 ] {
325 let (mut message, _headers) =
326 Message::from_armor_file(msg_file).expect("failed to parse message");
327
328 assert!(message.is_literal());
329 assert_eq!(message.as_data_vec().unwrap(), TEXT.as_bytes());
330 }
331
332 let res = Message::from_armor_file(
334 "./tests/unit-tests/partial-body-length/literal.packet-partial.256.asc",
335 );
336
337 #[cfg(not(feature = "malformed-artifact-compat"))]
338 assert!(res.is_err());
339
340 #[cfg(feature = "malformed-artifact-compat")]
341 assert!(res.is_ok());
342 }
343}