diff options
Diffstat (limited to 'pg/src/main/java/org/spongycastle/openpgp/PGPLiteralDataGenerator.java')
-rw-r--r-- | pg/src/main/java/org/spongycastle/openpgp/PGPLiteralDataGenerator.java | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralDataGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralDataGenerator.java new file mode 100644 index 00000000..9487238e --- /dev/null +++ b/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralDataGenerator.java @@ -0,0 +1,231 @@ +package org.spongycastle.openpgp; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.util.Strings; + +/** + * Generator for producing literal data packets. + * <p/> + * A PGPLiteralData is used by invoking one of the open functions to create an OutputStream that raw + * data can be supplied to for encoding: + * <ul> + * <li>If the length of the data to be written is known in advance, use + * {@link #open(OutputStream, char, String, long, Date)} to create a packet containing a single + * literal data object.</li> + * <li>If the length of the data is unknown, use + * {@link #open(OutputStream, char, String, Date, byte[])} to create a packet consisting of a series + * of literal data objects (partials).</li> + * </ul> + * <p/> + * A PGPLiteralDataGenerator is usually used to wrap the OutputStream + * {@link PGPEncryptedDataGenerator#open(OutputStream, byte[]) obtained} from a + * {@link PGPEncryptedDataGenerator} or a {@link PGPCompressedDataGenerator}. + * <p/> + * Once literal data has been written to the constructed OutputStream, writing of the object stream + * is completed by closing the OutputStream obtained from the <code>open()</code> method, or + * equivalently invoking {@link #close()} on this generator. + */ +public class PGPLiteralDataGenerator implements StreamGenerator +{ + /** Format tag for binary literal data */ + public static final char BINARY = PGPLiteralData.BINARY; + /** Format tag for textual literal data */ + public static final char TEXT = PGPLiteralData.TEXT; + /** Format tag for UTF-8 encoded textual literal data */ + public static final char UTF8 = PGPLiteralData.UTF8; + + /** + * The special name indicating a "for your eyes only" packet. + */ + // TODO: Not used? + public static final String CONSOLE = PGPLiteralData.CONSOLE; + + /** + * The special time for a modification time of "now" or + * the present time. + */ + public static final Date NOW = PGPLiteralData.NOW; + + private BCPGOutputStream pkOut; + private boolean oldFormat = false; + + /** + * Constructs a generator for literal data objects. + */ + public PGPLiteralDataGenerator() + { + } + + /** + * Constructs a generator for literal data objects, specifying to use new or old (PGP 2.6.x + * compatible) format. + * <p/> + * This can be used for compatibility with PGP 2.6.x. + * + * @param oldFormat <code>true</code> to use PGP 2.6.x compatible format. + */ + public PGPLiteralDataGenerator( + boolean oldFormat) + { + this.oldFormat = oldFormat; + } + + private void writeHeader( + OutputStream out, + char format, + byte[] encName, + long modificationTime) + throws IOException + { + out.write(format); + + out.write((byte)encName.length); + + for (int i = 0; i != encName.length; i++) + { + out.write(encName[i]); + } + + long modDate = modificationTime / 1000; + + out.write((byte)(modDate >> 24)); + out.write((byte)(modDate >> 16)); + out.write((byte)(modDate >> 8)); + out.write((byte)(modDate)); + } + + /** + * Open a literal data packet, returning a stream to store the data inside the packet. + * <p> + * The stream created can be closed off by either calling close() on the stream or close() on + * the generator. Closing the returned stream does not close off the OutputStream parameter out. + * + * @param out the underlying output stream to write the literal data packet to. + * @param format the format of the literal data that will be written to the output stream (one + * of {@link #BINARY}, {@link #TEXT} or {@link #UTF8}). + * @param name the name of the "file" to encode in the literal data object. + * @param length the length of the data that will be written. + * @param modificationTime the time of last modification we want stored. + */ + public OutputStream open( + OutputStream out, + char format, + String name, + long length, + Date modificationTime) + throws IOException + { + if (pkOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + byte[] encName = Strings.toUTF8ByteArray(name); + + pkOut = new BCPGOutputStream(out, PacketTags.LITERAL_DATA, length + 2 + encName.length + 4, oldFormat); + + writeHeader(pkOut, format, encName, modificationTime.getTime()); + + return new WrappedGeneratorStream(pkOut, this); + } + + /** + * Open a literal data packet, returning a stream to store the data inside the packet as an + * indefinite length stream. The stream is written out as a series of partial packets with a + * chunk size determined by the size of the passed in buffer. + * <p> + * The stream created can be closed off by either calling close() on the stream or close() on + * the generator. Closing the returned stream does not close off the OutputStream parameter out. + * + * <p> + * <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 bytes + * worth of the buffer will be used. + * + * @param out the underlying output stream to write the literal data packet to. + * @param format the format of the literal data that will be written to the output stream (one + * of {@link #BINARY}, {@link #TEXT} or {@link #UTF8}). + * @param name the name of the "file" to encode in the literal data object. + * @param modificationTime the time of last modification we want stored (will be stored to + * second level precision). + * @param buffer a buffer to use to buffer and write partial packets. The returned stream takes + * ownership of the buffer. + * + * @return the output stream to write data to. + * @throws IOException if an error occurs writing stream header information to the provider + * output stream. + * @throws IllegalStateException if this generator already has an open OutputStream. + */ + public OutputStream open( + OutputStream out, + char format, + String name, + Date modificationTime, + byte[] buffer) + throws IOException + { + if (pkOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + pkOut = new BCPGOutputStream(out, PacketTags.LITERAL_DATA, buffer); + + byte[] encName = Strings.toUTF8ByteArray(name); + + writeHeader(pkOut, format, encName, modificationTime.getTime()); + + return new WrappedGeneratorStream(pkOut, this); + } + + /** + * Open a literal data packet for the passed in File object, returning an output stream for + * saving the file contents. + * <p/> + * This method configures the generator to store the file contents in a single literal data + * packet, taking the filename and modification time from the file, but does not store the + * actual file data. + * <p/> + * The stream created can be closed off by either calling close() on the stream or close() on + * the generator. Closing the returned stream does not close off the OutputStream parameter out. + * + * @param out the underlying output stream to write the literal data packet to. + * @param format the format of the literal data that will be written to the output stream (one + * of {@link #BINARY}, {@link #TEXT} or {@link #UTF8}). + * @param file the file to determine the length and filename from. + * @return the output stream to write data to. + * @throws IOException if an error occurs writing stream header information to the provider + * output stream. + * @throws IllegalStateException if this generator already has an open OutputStream. + */ + public OutputStream open( + OutputStream out, + char format, + File file) + throws IOException + { + return open(out, format, file.getName(), file.length(), new Date(file.lastModified())); + } + + /** + * Close the literal data packet - this is equivalent to calling close on the stream + * returned by the open() method. + * + * @throws IOException + */ + public void close() + throws IOException + { + if (pkOut != null) + { + pkOut.finish(); + pkOut.flush(); + pkOut = null; + } + } +} |