diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2018-09-10 18:00:34 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2018-09-10 18:08:19 +0200 |
commit | ee071fb533ba2c0a384e09dd8915e9eab363b5e7 (patch) | |
tree | c53254fc44e4813ba654924edd51ccb0b2de3ba1 /openpgp/src/armor.rs | |
parent | 3d2edcea18b1e7e832dd958bd248b36865138aa6 (diff) |
openpgp: Do not emit empty ASCII armor frame if writer is unused.
- In the frontend, we construct an armor writer, then hand it to
e.g. the encryptor. Currently, if the encryptor fails for some
reason without emitting any data, the armor frame is still written
when the armor writer is dropped.
- Defer writing of the framing and the headers until something is
actually written, and in case the writer is dropped before
anything is written, just don't write anything.
- Fixes #97.
Diffstat (limited to 'openpgp/src/armor.rs')
-rw-r--r-- | openpgp/src/armor.rs | 67 |
1 files changed, 60 insertions, 7 deletions
diff --git a/openpgp/src/armor.rs b/openpgp/src/armor.rs index f7e2db60..7ba91108 100644 --- a/openpgp/src/armor.rs +++ b/openpgp/src/armor.rs @@ -29,7 +29,7 @@ use buffered_reader::{ BufferedReader, BufferedReaderGeneric, BufferedReaderMemory, }; use std::fs::File; -use std::io::{Read, Write}; +use std::io::{Cursor, Read, Write}; use std::io::{Result, Error, ErrorKind}; use std::path::Path; use std::cmp::min; @@ -140,6 +140,8 @@ pub struct Writer<W: Write> { stash: Vec<u8>, column: usize, crc: CRC, + epilogue: Vec<u8>, + dirty: bool, finalized: bool, } @@ -181,21 +183,37 @@ impl<W: Write> Writer<W> { stash: Vec::<u8>::with_capacity(2), column: 0, crc: CRC::new(), + epilogue: Vec::with_capacity(128), + dirty: false, finalized: false, }; - write!(w.sink, "{}{}", w.kind.begin(), LINE_ENDING)?; + { + let mut cur = Cursor::new(&mut w.epilogue); + write!(&mut cur, "{}{}", kind.begin(), LINE_ENDING)?; - for h in headers { - write!(w.sink, "{}: {}{}", h.0, h.1, LINE_ENDING)?; - } + for h in headers { + write!(&mut cur, "{}: {}{}", h.0, h.1, LINE_ENDING)?; + } - // A blank line separates the headers from the body. - write!(w.sink, "{}", LINE_ENDING)?; + // A blank line separates the headers from the body. + write!(&mut cur, "{}", LINE_ENDING)?; + } Ok(w) } + fn write_epilogue(&mut self) -> Result<()> { + if ! self.dirty { + self.dirty = true; + self.sink.write_all(&self.epilogue)?; + // Release memory. + self.epilogue.clear(); + self.epilogue.shrink_to_fit(); + } + Ok(()) + } + /// Writes the footer. /// /// No more data can be written after this call. If this is not @@ -206,6 +224,12 @@ impl<W: Write> Writer<W> { return Err(Error::new(ErrorKind::BrokenPipe, "Writer is finalized.")); } + if ! self.dirty { + // No data was written to us, don't emit anything. + return Ok(()); + } + self.write_epilogue()?; + // Write any stashed bytes and pad. if self.stash.len() > 0 { self.sink.write_all(base64::encode_config(&self.stash, @@ -250,6 +274,8 @@ impl<W: Write> Write for Writer<W> { return Err(Error::new(ErrorKind::BrokenPipe, "Writer is finalized.")); } + self.write_epilogue()?; + // Update CRC on the unencoded data. self.crc.update(buf); @@ -1031,6 +1057,7 @@ mod test { let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf, Kind::File, &[][..]).unwrap(); + w.write(&[]).unwrap(); // Avoid zero-length optimization. w.write_all(&bin).unwrap(); } assert_eq!(String::from_utf8_lossy(&buf), @@ -1052,6 +1079,7 @@ mod test { let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf, Kind::File, &[][..]).unwrap(); + w.write(&[]).unwrap(); // Avoid zero-length optimization. for (i, _) in bin.iter().enumerate() { w.write(&bin[i..i+1]).unwrap(); } @@ -1061,6 +1089,31 @@ mod test { } } + #[test] + fn drop_writer() { + // No ASCII frame shall be emitted if the writer is dropped + // unused. + let mut buf = Vec::new(); + { + drop(Writer::new(&mut buf, Kind::File, &[][..]).unwrap()); + } + assert!(buf.is_empty()); + + // However, if the user insists, we will encode a zero-byte + // string. + let mut buf = Vec::new(); + { + let mut w = Writer::new(&mut buf, Kind::File, &[][..]).unwrap(); + w.write(&[]).unwrap(); + } + assert_eq!( + &buf[..], + &b"-----BEGIN PGP ARMORED FILE-----\n\ + \n\ + =twTO\n\ + -----END PGP ARMORED FILE-----\n"[..]); + } + use super::Reader; #[test] |