summaryrefslogtreecommitdiffstats
path: root/openpgp/src/armor.rs
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-09-10 18:00:34 +0200
committerJustus Winter <justus@sequoia-pgp.org>2018-09-10 18:08:19 +0200
commitee071fb533ba2c0a384e09dd8915e9eab363b5e7 (patch)
treec53254fc44e4813ba654924edd51ccb0b2de3ba1 /openpgp/src/armor.rs
parent3d2edcea18b1e7e832dd958bd248b36865138aa6 (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.rs67
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]