package de.idyl.winzipaes.impl; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.zip.ZipEntry; /** * Write zip entries to Zip-File, encrypted or not encrypted. * * @author olaf@merkert.de */ public class ExtZipOutputStream implements ZipConstants { public ExtZipOutputStream(File file) throws IOException { out = new FileOutputStream(file); } public ExtZipOutputStream(OutputStream out) { this.out = out; } protected String comment; protected OutputStream out; /** number of bytes written to out */ protected int written; public int getWritten() { return this.written; } public void writeBytes(byte[] b) throws IOException { out.write(b); written += b.length; } public void writeShort(int v) throws IOException { out.write((v >>> 0) & 0xff); out.write((v >>> 8) & 0xff); written += 2; } public void writeInt(long v) throws IOException { out.write((int) ((v >>> 0) & 0xff)); out.write((int) ((v >>> 8) & 0xff)); out.write((int) ((v >>> 16) & 0xff)); out.write((int) ((v >>> 24) & 0xff)); written += 4; } public void writeBytes(byte[] b, int off, int len) throws IOException { out.write(b, off, len); written += len; } // -------------------------------------------------------------------------- protected final static short ZIP_VERSION = 20; // version set by // java.util.zip protected void writeFileInfo(ExtZipEntry entry) throws IOException { writeShort(ZIP_VERSION); // version needed to extract // general purpose bit flag - 0x0001 indicates encryption 2 bytes writeShort(entry.getFlag()); writeShort(entry.getPrimaryCompressionMethod()); // primary compression // method - // 0x63==encryption writeInt(entry.getDosTime()); // 2 bytes last mod file time + 2 bytes // last mod file date writeInt(entry.getCrc()); // 28 bytes is the encryption overhead (caused by 256-bit AES key) // 2 bytes pwVerification + 16 bytes SALT + 10 bytes AUTHENTICATION writeInt((int) entry.getCompressedSize()); // compressed size writeInt((int) entry.getSize()); // uncompressed size writeShort(entry.getName().length()); // file name length if (entry.getExtra() != null) { writeShort(entry.getExtra().length); // extra field length } else { writeShort(0); } } private List<ExtZipEntry> entries = new ArrayList<ExtZipEntry>(); protected void writeDirEntry(ExtZipEntry entry) throws IOException { writeInt(CENSIG); // writeBytes( new byte[] { 0x50, 0x4b, 0x01, 0x02 } // ); // directory signature writeShort(ZIP_VERSION); // version made by writeFileInfo(entry); writeShort(0x00); // file comment length 2 bytes writeShort(0x00); // disk number start (unused) 2 bytes writeShort(0x00); // internal file attributes (unsued) 2 bytes writeInt(0x00); // external file attributes (unused) 4 bytes writeInt(entry.getOffset()); // relative offset of local header 4 bytes writeBytes(entry.getName().getBytes("iso-8859-1")); writeExtraBytes(entry); } protected void writeExtraBytes(ZipEntry entry) throws IOException { byte[] extraBytes = entry.getExtra(); if (extraBytes != null) { writeBytes(extraBytes); } } // -------------------------------------------------------------------------- public void putNextEntry(ExtZipEntry entry) throws IOException { entries.add(entry); entry.setOffset(written); // file header signature writeInt(LOCSIG); writeFileInfo(entry); writeBytes(entry.getName().getBytes("iso-8859-1")); writeExtraBytes(entry); } /** * Finishes writing the contents of the ZIP output stream. */ public void finish() throws IOException { int dirOffset = written; // central directory (at end of zip file) // starts here int startOfCentralDirectory = written; Iterator<ExtZipEntry> it = entries.iterator(); while (it.hasNext()) { ExtZipEntry entry = it.next(); writeDirEntry(entry); } int centralDirectorySize = written - startOfCentralDirectory; writeInt(ENDSIG); // end of central dir signature 4 bytes writeShort(0x00); // number of this disk 2 bytes writeShort(0x00); // number of the disk with the start of the central directory 2 bytes writeShort(entries.size()); // total number of entries in central directory on this disk 2 bytes writeShort(entries.size()); // total number of entries in the central directory 2 bytes writeInt(centralDirectorySize); // size of the central directory 4 bytes writeInt(dirOffset); // offset of start of central dir, with respect to starting disk 4 bytes byte[] commentBytes = this.comment!=null ? this.comment.getBytes() : new byte[0]; writeShort(commentBytes.length); // .ZIP file comment length 2 bytes if( commentBytes.length>0 ) { writeBytes(commentBytes); } out.close(); } public void close() throws IOException { out.close(); } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } }