001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Charles Reich
027: */
028:
029: package com.caucho.quercus.lib.zip;
030:
031: import com.caucho.quercus.lib.file.BinaryInput;
032:
033: import java.io.IOException;
034: import java.util.zip.ZipEntry;
035:
036: /**
037: * Reads the zip header and prepares zip entries.
038: */
039: public class ZipDirectory {
040: private BinaryInput _in;
041: private byte[] _tmpBuf;
042: private ZipEntry _currentEntry;
043:
044: private boolean _eof;
045: private boolean _ddescriptor;
046:
047: public ZipDirectory(BinaryInput in) {
048: _in = in;
049: _tmpBuf = new byte[32];
050: _eof = false;
051: }
052:
053: /**
054: * Closes the previous entry and returns the next entry's metadata.
055: */
056: public QuercusZipEntry zip_read() throws IOException {
057: closeEntry();
058:
059: long position = _in.getPosition();
060: ZipEntry entry = readEntry();
061:
062: if (entry == null)
063: return null;
064: else
065: return new QuercusZipEntry(entry, _in.openCopy(), position);
066: }
067:
068: /**
069: * Reads the next entry's metadata from the current stream position.
070: */
071: protected ZipEntry readEntry() throws IOException {
072: if (_eof || _currentEntry != null)
073: return null;
074:
075: int sublen = _in.read(_tmpBuf, 0, 30);
076: if (sublen < 30) {
077: _eof = true;
078: return null;
079: }
080:
081: // Zip file signature check
082: if ((((_tmpBuf[3] & 0xff) << 24) | ((_tmpBuf[2] & 0xff) << 16)
083: | ((_tmpBuf[1] & 0xff) << 8) | (_tmpBuf[0] & 0xff)) != 0x04034b50) {
084: _eof = true;
085: return null;
086: }
087:
088: // Extra data descriptors after the compressed data
089: if ((_tmpBuf[6] & 0x04) == 0x04)
090: _ddescriptor = true;
091: else
092: _ddescriptor = false;
093:
094: int compressionMethod = (_tmpBuf[8] & 0xff)
095: | ((_tmpBuf[9] & 0xff) << 8);
096:
097: //if (compressionMethod != 0 && compressionMethod != 8)
098: // throw new IOException("Unsupported zip compression method (" + compressionMethod + ").");
099:
100: long crc32 = _tmpBuf[14] & 0xff;
101: crc32 |= (_tmpBuf[15] & 0xff) << 8;
102: crc32 |= (_tmpBuf[16] & 0xff) << 16;
103: crc32 |= ((long) _tmpBuf[17] & 0xff) << 24;
104:
105: long compressedSize = _tmpBuf[18] & 0xff;
106: compressedSize |= (_tmpBuf[19] & 0xff) << 8;
107: compressedSize |= (_tmpBuf[20] & 0xff) << 16;
108: compressedSize |= ((long) _tmpBuf[21] & 0xff) << 24;
109:
110: long uncompressedSize = _tmpBuf[22] & 0xff;
111: uncompressedSize |= (_tmpBuf[23] & 0xff) << 8;
112: uncompressedSize |= (_tmpBuf[24] & 0xff) << 16;
113: uncompressedSize |= ((long) _tmpBuf[25] & 0xff) << 24;
114:
115: int filenameLength = _tmpBuf[26] & 0xff;
116: filenameLength |= (_tmpBuf[27] & 0xff) << 8;
117:
118: int extraLength = _tmpBuf[28] & 0xff;
119: extraLength |= (_tmpBuf[29] & 0xff) << 8;
120:
121: // XXX: correct char encoding?
122: String name;
123: if (filenameLength <= _tmpBuf.length) {
124: sublen = _in.read(_tmpBuf, 0, filenameLength);
125: if (sublen < filenameLength)
126: return null;
127: name = new String(_tmpBuf, 0, sublen);
128: } else {
129: byte[] buffer = new byte[filenameLength];
130: sublen = _in.read(buffer, 0, buffer.length);
131: if (sublen < filenameLength)
132: return null;
133: name = new String(buffer, 0, sublen);
134: }
135:
136: if (extraLength > 0)
137: skip(extraLength);
138:
139: ZipEntry entry = new ZipEntry(name);
140: entry.setMethod(compressionMethod);
141: entry.setCrc(crc32);
142: entry.setCompressedSize(compressedSize);
143: entry.setSize(uncompressedSize);
144:
145: _currentEntry = entry;
146: return entry;
147: }
148:
149: private void skip(long len) throws IOException {
150: while (len-- > 0 && _in.read() != -1) {
151: }
152: }
153:
154: /**
155: * Positions stream to beginning of next entry
156: */
157: protected void closeEntry() throws IOException {
158: if (_currentEntry == null)
159: return;
160:
161: long length = _currentEntry.getCompressedSize();
162:
163: if (_ddescriptor)
164: length += 12;
165:
166: skip(length);
167: _currentEntry = null;
168: }
169:
170: public boolean zip_close() {
171: _in.close();
172:
173: return true;
174: }
175:
176: public String toString() {
177: return "ZipDirectory[]";
178: }
179: }
|