package org.bouncycastle.openpgp; import org.bouncycastle.apache.bzip2.CBZip2OutputStream; import org.bouncycastle.bcpg.BCPGOutputStream; import org.bouncycastle.bcpg.CompressionAlgorithmTags; import org.bouncycastle.bcpg.PacketTags; import java.io.IOException; import java.io.OutputStream; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; /** *class for producing compressed data packets. */ public class PGPCompressedDataGenerator implements CompressionAlgorithmTags, StreamGenerator { private int algorithm; private int compression; private OutputStream out; private OutputStream dOut; private BCPGOutputStream pkOut; public PGPCompressedDataGenerator( int algorithm) { this(algorithm, Deflater.DEFAULT_COMPRESSION); } public PGPCompressedDataGenerator( int algorithm, int compression) { if (algorithm != PGPCompressedData.UNCOMPRESSED && algorithm != PGPCompressedData.ZIP && algorithm != PGPCompressedData.ZLIB && algorithm != PGPCompressedData.BZIP2) { throw new IllegalArgumentException("unknown compression algorithm"); } if (compression != Deflater.DEFAULT_COMPRESSION) { if ((compression < 0) || (compression > 9)) { throw new IllegalArgumentException("unknown compression level: " + compression); } } this.algorithm = algorithm; this.compression = compression; } /** * Return an outputstream which will save the data being written to * the compressed object. * <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 underlying OutputStream to be used. * @return OutputStream * @throws IOException, IllegalStateException */ public OutputStream open( OutputStream out) throws IOException { if (dOut != null) { throw new IllegalStateException("generator already in open state"); } this.out = out; switch (algorithm) { case PGPCompressedData.ZIP: pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA); pkOut.write(PGPCompressedData.ZIP); dOut = new DeflaterOutputStream(pkOut, new Deflater(compression, true)); break; case PGPCompressedData.ZLIB: pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA); pkOut.write(PGPCompressedData.ZLIB); dOut = new DeflaterOutputStream(pkOut, new Deflater(compression)); break; case BZIP2: pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA); pkOut.write(PGPCompressedData.BZIP2); dOut = new CBZip2OutputStream(pkOut); break; case PGPCompressedData.UNCOMPRESSED: pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA); pkOut.write(PGPCompressedData.UNCOMPRESSED); dOut = pkOut; break; default: throw new IllegalStateException("generator not initialised"); } return new WrappedGeneratorStream(dOut, this); } /** * Return an outputstream which will compress the data as it is written * to it. The stream will be written out in chunks according to 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. * </p> * <p> * <b>Note</b>: using this may break compatability with RFC 1991 compliant tools. Only recent OpenPGP * implementations are capable of accepting these streams. * </p> * * @param out underlying OutputStream to be used. * @param buffer the buffer to use. * @return OutputStream * @throws IOException * @throws PGPException */ public OutputStream open( OutputStream out, byte[] buffer) throws IOException, PGPException { if (dOut != null) { throw new IllegalStateException("generator already in open state"); } this.out = out; switch (algorithm) { case PGPCompressedData.ZIP: pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA, buffer); pkOut.write(PGPCompressedData.ZIP); dOut = new DeflaterOutputStream(pkOut, new Deflater(compression, true)); break; case PGPCompressedData.ZLIB: pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA, buffer); pkOut.write(PGPCompressedData.ZLIB); dOut = new DeflaterOutputStream(pkOut, new Deflater(compression)); break; case PGPCompressedData.BZIP2: pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA, buffer); pkOut.write(PGPCompressedData.BZIP2); dOut = new CBZip2OutputStream(pkOut); break; case PGPCompressedData.UNCOMPRESSED: pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA, buffer); pkOut.write(PGPCompressedData.UNCOMPRESSED); dOut = pkOut; break; default: throw new IllegalStateException("generator not initialised"); } return new WrappedGeneratorStream(dOut, this); } /** * Close the compressed object - this is equivalent to calling close on the stream * returned by the open() method. * * @throws IOException */ public void close() throws IOException { if (dOut != null) { if (dOut instanceof DeflaterOutputStream) { DeflaterOutputStream dfOut = (DeflaterOutputStream)dOut; dfOut.finish(); } else if (dOut instanceof CBZip2OutputStream) { CBZip2OutputStream cbOut = (CBZip2OutputStream)dOut; cbOut.finish(); } dOut.flush(); pkOut.finish(); pkOut.flush(); out.flush(); dOut = null; pkOut = null; out = null; } } }