public InputStream getInputStream(final ZipEntry ze) throws IOException, ZipException { if (!(ze instanceof Entry)) { return null; } final OffsetEntry offsetEntry = ((Entry) ze).getOffsetEntry(); ZipUtil.checkRequestedFeatures(ze); final long start = offsetEntry.dataOffset; final BoundedInputStream bis = new BoundedInputStream(start, ze.getCompressedSize()); switch (ze.getMethod()) { case 0: { return bis; } case 8: { bis.addDummy(); final Inflater inflater = new Inflater(true); return new InflaterInputStream(bis, inflater) { public void close() throws IOException { super.close(); inflater.end(); } }; } default: { throw new ZipException("Found unsupported compression method " + ze.getMethod()); } } }
// TODO - zipEntry might use extended local header protected void add(ZipEntry zipEntry, ZipFileEntryInputStream zipData, String password) throws IOException, UnsupportedEncodingException { AESEncrypter aesEncrypter = new AESEncrypterBC(password.getBytes("iso-8859-1")); ExtZipEntry entry = new ExtZipEntry(zipEntry.getName()); entry.setMethod(zipEntry.getMethod()); entry.setSize(zipEntry.getSize()); entry.setCompressedSize(zipEntry.getCompressedSize() + 28); entry.setTime(zipEntry.getTime()); entry.initEncryptedEntry(); zipOS.putNextEntry(entry); // ZIP-file data contains: 1. salt 2. pwVerification 3. encryptedContent 4. authenticationCode zipOS.writeBytes(aesEncrypter.getSalt()); zipOS.writeBytes(aesEncrypter.getPwVerification()); byte[] data = new byte[1024]; int read = zipData.read(data); while (read != -1) { aesEncrypter.encrypt(data, read); zipOS.writeBytes(data, 0, read); read = zipData.read(data); } byte[] finalAuthentication = aesEncrypter.getFinalAuthentication(); if (LOG.isLoggable(Level.FINE)) { LOG.fine( "finalAuthentication=" + Arrays.toString(finalAuthentication) + " at pos=" + zipOS.getWritten()); } zipOS.writeBytes(finalAuthentication); }
public ZipRepository(final InputStream in, final MimeRegistry mimeRegistry) throws IOException { this(mimeRegistry); final ZipInputStream zipIn = new ZipInputStream(in); try { ZipEntry nextEntry = zipIn.getNextEntry(); while (nextEntry != null) { final String[] buildName = RepositoryUtilities.splitPath(nextEntry.getName(), "/"); if (nextEntry.isDirectory()) { root.updateDirectoryEntry(buildName, 0, nextEntry); } else { final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final Deflater def = new Deflater(nextEntry.getMethod()); try { final DeflaterOutputStream dos = new DeflaterOutputStream(bos, def); try { IOUtils.getInstance().copyStreams(zipIn, dos); dos.flush(); } finally { dos.close(); } } finally { def.end(); } root.updateEntry(buildName, 0, nextEntry, bos.toByteArray()); } zipIn.closeEntry(); nextEntry = zipIn.getNextEntry(); } } finally { zipIn.close(); } }
/** * Returns the options of an archive. * * @param ctx query context * @return entries * @throws QueryException query exception */ private FElem options(final QueryContext ctx) throws QueryException { final B64 archive = (B64) checkType(checkItem(expr[0], ctx), AtomType.B64); String format = null; int level = -1; final ArchiveIn arch = ArchiveIn.get(archive.input(info), info); try { format = arch.format(); while (arch.more()) { final ZipEntry ze = arch.entry(); if (ze.isDirectory()) continue; level = ze.getMethod(); break; } } catch (final IOException ex) { Util.debug(ex); ARCH_FAIL.thrw(info, ex); } finally { arch.close(); } // create result element final FElem e = new FElem(Q_OPTIONS, NS); if (format != null) e.add(new FElem(Q_FORMAT).add(Q_VALUE, format)); if (level >= 0) { final byte[] lvl = level == 8 ? DEFLATE : level == 0 ? STORED : UNKNOWN; e.add(new FElem(Q_ALGORITHM).add(Q_VALUE, lvl)); } return e; }
/** * Appends a zip64 extended info record to the extras contained in {@code ze}. If {@code ze} * contains no extras, a new extras array is created. */ public static void insertZip64ExtendedInfoToExtras(ZipEntry ze) throws ZipException { final byte[] output; // We always write the size, uncompressed size and local rel header offset in all our // Zip64 extended info headers (in both the local file header as well as the central // directory). We always omit the disk number because we don't support spanned // archives anyway. // // 2 bytes : Zip64 Extended Info Header ID // 2 bytes : Zip64 Extended Info Field Size. // 8 bytes : Uncompressed size // 8 bytes : Compressed size // 8 bytes : Local header rel offset. // ---------- // 28 bytes : total final int extendedInfoSize = 28; if (ze.extra == null) { output = new byte[extendedInfoSize]; } else { // If the existing extras are already too big, we have no choice but to throw // an error. if (ze.extra.length + extendedInfoSize > 65535) { throw new ZipException("No space in extras for zip64 extended entry info"); } // We copy existing extras over and put the zip64 extended info at the beginning. This // is to avoid breakages in the presence of "old style" extras which don't contain // headers and lengths. The spec is again silent about these inconsistencies. // // This means that people that for ZipOutputStream users, the value ZipEntry.getExtra // after an entry is written will be different from before. This shouldn't be an issue // in practice. output = new byte[ze.extra.length + extendedInfoSize]; System.arraycopy(ze.extra, 0, output, extendedInfoSize, ze.extra.length); } ByteBuffer bb = ByteBuffer.wrap(output).order(ByteOrder.LITTLE_ENDIAN); bb.putShort(ZIP64_EXTENDED_INFO_HEADER_ID); // We subtract four because extendedInfoSize includes the ID and field // size itself. bb.putShort((short) (extendedInfoSize - 4)); if (ze.getMethod() == ZipEntry.STORED) { bb.putLong(ze.size); bb.putLong(ze.compressedSize); } else { // Store these fields in the data descriptor instead. bb.putLong(0); // size. bb.putLong(0); // compressed size. } // The offset is only relevant in the central directory entry, but we write it out here // anyway, since we know what it is. bb.putLong(ze.localHeaderRelOffset); ze.extra = output; }
public AesZipEntry(ZipEntry zipEntry) { super(zipEntry.getName()); super.setMethod(zipEntry.getMethod()); super.setSize(zipEntry.getSize()); super.setCompressedSize(zipEntry.getCompressedSize() + 28); super.setTime(zipEntry.getTime()); flag |= 1; // bit0 - encrypted // flag |= 8; // bit3 - use data descriptor }
/** * Dumps a zip entry into a string. * * @param ze a ZipEntry */ private String dumpZipEntry(ZipEntry ze) { StringBuffer sb = new StringBuffer(); if (ze.isDirectory()) { sb.append("d "); } else { sb.append("f "); } if (ze.getMethod() == ZipEntry.STORED) { sb.append("stored "); } else { sb.append("defalted "); } sb.append(ze.getName()); sb.append("\t"); sb.append("" + ze.getSize()); if (ze.getMethod() == ZipEntry.DEFLATED) { sb.append("/" + ze.getCompressedSize()); } return (sb.toString()); }
/** Adds a new file entry to the ZIP output stream. */ void addFile(ZipOutputStream zos, File file) throws IOException { String name = file.getPath(); boolean isDir = file.isDirectory(); if (isDir) { name = name.endsWith(File.separator) ? name : (name + File.separator); } name = entryName(name); if (name.equals("") || name.equals(".") || name.equals(zname)) { return; } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME)) && !Mflag) { if (vflag) { output(formatMsg("out.ignore.entry", name)); } return; } long size = isDir ? 0 : file.length(); if (vflag) { out.print(formatMsg("out.adding", name)); } ZipEntry e = new ZipEntry(name); e.setTime(file.lastModified()); if (size == 0) { e.setMethod(ZipEntry.STORED); e.setSize(0); e.setCrc(0); } else if (flag0) { crc32File(e, file); } zos.putNextEntry(e); if (!isDir) { copy(file, zos); } zos.closeEntry(); /* report how much compression occurred. */ if (vflag) { size = e.getSize(); long csize = e.getCompressedSize(); out.print(formatMsg2("out.size", String.valueOf(size), String.valueOf(csize))); if (e.getMethod() == ZipEntry.DEFLATED) { long ratio = 0; if (size != 0) { ratio = ((size - csize) * 100) / size; } output(formatMsg("out.deflated", String.valueOf(ratio))); } else { output(getMsg("out.stored")); } } }
/** * Creates a new zip entry with fields taken from the specified zip entry. * * <p>Assumes the entry represents a directory if and only if the name ends with a forward slash * "/". * * @param entry the entry to get fields from * @throws ZipException on error */ public ZipArchiveEntry(java.util.zip.ZipEntry entry) throws ZipException { super(entry); setName(entry.getName()); byte[] extra = entry.getExtra(); if (extra != null) { setExtraFields( ExtraFieldUtils.parse(extra, true, ExtraFieldUtils.UnparseableExtraField.READ)); } else { // initializes extra data to an empty byte array setExtra(); } setMethod(entry.getMethod()); this.size = entry.getSize(); }
/** * Extracts next entry from JAR file, creating directories as needed. If the entry is for a * directory which doesn't exist prior to this invocation, returns that entry, otherwise returns * null. */ ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException { ZipEntry rc = null; String name = e.getName(); File f = new File(e.getName().replace('/', File.separatorChar)); if (e.isDirectory()) { if (f.exists()) { if (!f.isDirectory()) { throw new IOException(formatMsg("error.create.dir", f.getPath())); } } else { if (!f.mkdirs()) { throw new IOException(formatMsg("error.create.dir", f.getPath())); } else { rc = e; } } if (vflag) { output(formatMsg("out.create", name)); } } else { if (f.getParent() != null) { File d = new File(f.getParent()); if (!d.exists() && !d.mkdirs() || !d.isDirectory()) { throw new IOException(formatMsg("error.create.dir", d.getPath())); } } try { copy(is, f); } finally { if (is instanceof ZipInputStream) ((ZipInputStream) is).closeEntry(); else is.close(); } if (vflag) { if (e.getMethod() == ZipEntry.DEFLATED) { output(formatMsg("out.inflated", name)); } else { output(formatMsg("out.extracted", name)); } } } if (!useExtractionTime) { long lastModified = e.getTime(); if (lastModified != -1) { f.setLastModified(lastModified); } } return rc; }
public void decodeUnknownFiles(ExtFile apkFile, File outDir, ResTable resTable) throws AndrolibException { LOGGER.info("Copying unknown files..."); File unknownOut = new File(outDir, UNK_DIRNAME); ZipEntry invZipFile; // have to use container of ZipFile to help identify compression type // with regular looping of apkFile for easy copy try { Directory unk = apkFile.getDirectory(); ZipFile apkZipFile = new ZipFile(apkFile.getAbsolutePath()); // loop all items in container recursively, ignoring any that are pre-defined by aapt Set<String> files = unk.getFiles(true); for (String file : files) { if (!isAPKFileNames(file) && !file.endsWith(".dex")) { // copy file out of archive into special "unknown" folder unk.copyToDir(unknownOut, file); try { invZipFile = apkZipFile.getEntry(file); // lets record the name of the file, and its compression type // so that we may re-include it the same way if (invZipFile != null) { mResUnknownFiles.addUnknownFileInfo( invZipFile.getName(), String.valueOf(invZipFile.getMethod())); } } catch (NullPointerException ignored) { } } } apkZipFile.close(); } catch (DirectoryException | IOException ex) { throw new AndrolibException(ex); } }
/** * Parse the zip64 extended info record from the extras present in {@code ze}. * * <p>If {@code fromCentralDirectory} is true, we assume we're parsing a central directory record. * We assume a local file header otherwise. The difference between the two is that a central * directory entry is required to be complete, whereas a local file header isn't. This is due to * the presence of an optional data descriptor after the file content. * * @return {@code} true iff. a zip64 extended info record was found. */ public static boolean parseZip64ExtendedInfo(ZipEntry ze, boolean fromCentralDirectory) throws ZipException { int extendedInfoSize = -1; int extendedInfoStart = -1; // If this file contains a zip64 central directory locator, entries might // optionally contain a zip64 extended information extra entry. if (ze.extra != null && ze.extra.length > 0) { // Extensible data fields are of the form header1+data1 + header2+data2 and so // on, where each header consists of a 2 byte header ID followed by a 2 byte size. // We need to iterate through the entire list of headers to find the header ID // for the zip64 extended information extra field (0x0001). final ByteBuffer buf = ByteBuffer.wrap(ze.extra).order(ByteOrder.LITTLE_ENDIAN); extendedInfoSize = getZip64ExtendedInfoSize(buf); if (extendedInfoSize != -1) { extendedInfoStart = buf.position(); try { // The size & compressed size only make sense in the central directory *or* if // we know them beforehand. If we don't know them beforehand, they're stored in // the data descriptor and should be read from there. // // Note that the spec says that the local file header "MUST" contain the // original and compressed size fields. We don't care too much about that. // The spec claims that the order of fields is fixed anyway. if (fromCentralDirectory || (ze.getMethod() == ZipEntry.STORED)) { if (ze.size == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) { ze.size = buf.getLong(); } if (ze.compressedSize == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) { ze.compressedSize = buf.getLong(); } } // The local header offset is significant only in the central directory. It makes no // sense within the local header itself. if (fromCentralDirectory) { if (ze.localHeaderRelOffset == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) { ze.localHeaderRelOffset = buf.getLong(); } } } catch (BufferUnderflowException bue) { ZipException zipException = new ZipException("Error parsing extended info"); zipException.initCause(bue); throw zipException; } } } // This entry doesn't contain a zip64 extended information data entry header. // We have to check that the compressedSize / size / localHeaderRelOffset values // are valid and don't require the presence of the extended header. if (extendedInfoSize == -1) { if (ze.compressedSize == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE || ze.size == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE || ze.localHeaderRelOffset == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) { throw new ZipException( "File contains no zip64 extended information: " + "name=" + ze.name + "compressedSize=" + ze.compressedSize + ", size=" + ze.size + ", localHeader=" + ze.localHeaderRelOffset); } return false; } else { // If we're parsed the zip64 extended info header, we remove it from the extras // so that applications that set their own extras will see the data they set. // This is an unfortunate workaround needed due to a gap in the spec. The spec demands // that extras are present in the "extensible" format, which means that each extra field // must be prefixed with a header ID and a length. However, earlier versions of the spec // made no mention of this, nor did any existing API enforce it. This means users could // set "free form" extras without caring very much whether the implementation wanted to // extend or add to them. // The start of the extended info header. final int extendedInfoHeaderStart = extendedInfoStart - 4; // The total size of the extended info, including the header. final int extendedInfoTotalSize = extendedInfoSize + 4; final int extrasLen = ze.extra.length - extendedInfoTotalSize; byte[] extrasWithoutZip64 = new byte[extrasLen]; System.arraycopy(ze.extra, 0, extrasWithoutZip64, 0, extendedInfoHeaderStart); System.arraycopy( ze.extra, extendedInfoHeaderStart + extendedInfoTotalSize, extrasWithoutZip64, extendedInfoHeaderStart, (extrasLen - extendedInfoHeaderStart)); ze.extra = extrasWithoutZip64; return true; } }
// =============================================================================
/** Updates an existing jar file. */ boolean update(InputStream in, OutputStream out, InputStream newManifest, JarIndex jarIndex) throws IOException { ZipInputStream zis = new ZipInputStream(in); ZipOutputStream zos = new JarOutputStream(out); ZipEntry e = null; boolean foundManifest = false; boolean updateOk = true; if (jarIndex != null) { addIndex(jarIndex, zos); } // put the old entries first, replace if necessary while ((e = zis.getNextEntry()) != null) { String name = e.getName(); boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME); if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME)) || (Mflag && isManifestEntry)) { continue; } else if (isManifestEntry && ((newManifest != null) || (ename != null) || (pname != null))) { foundManifest = true; if (newManifest != null) { // Don't read from the newManifest InputStream, as we // might need it below, and we can't re-read the same data // twice. FileInputStream fis = new FileInputStream(mname); boolean ambiguous = isAmbiguousMainClass(new Manifest(fis)); fis.close(); if (ambiguous) { return false; } } // Update the manifest. Manifest old = new Manifest(zis); if (newManifest != null) { old.read(newManifest); } if (!updateManifest(old, zos)) { return false; } } else { if (!entryMap.containsKey(name)) { // copy the old stuff // do our own compression ZipEntry e2 = new ZipEntry(name); e2.setMethod(e.getMethod()); e2.setTime(e.getTime()); e2.setComment(e.getComment()); e2.setExtra(e.getExtra()); if (e.getMethod() == ZipEntry.STORED) { e2.setSize(e.getSize()); e2.setCrc(e.getCrc()); } zos.putNextEntry(e2); copy(zis, zos); } else { // replace with the new files File f = entryMap.get(name); addFile(zos, f); entryMap.remove(name); entries.remove(f); } } } // add the remaining new files for (File f : entries) { addFile(zos, f); } if (!foundManifest) { if (newManifest != null) { Manifest m = new Manifest(newManifest); updateOk = !isAmbiguousMainClass(m); if (updateOk) { if (!updateManifest(m, zos)) { updateOk = false; } } } else if (ename != null || pname != null) { if (!updateManifest(new Manifest(), zos)) { updateOk = false; } } } zis.close(); zos.close(); return updateOk; }
protected void processJarFile(final File file) throws Exception { if (verbose) { log("processing " + file.toURI()); } final File tempFile = File.createTempFile(file.getName(), null, new File(file.getAbsoluteFile().getParent())); try { final ZipInputStream zip = new ZipInputStream(new FileInputStream(file)); try { final FileOutputStream fout = new FileOutputStream(tempFile); try { final ZipOutputStream out = new ZipOutputStream(fout); ZipEntry entry; while ((entry = zip.getNextEntry()) != null) { byte bytes[] = getBytes(zip); if (!entry.isDirectory()) { final DataInputStream din = new DataInputStream(new ByteArrayInputStream(bytes)); if (din.readInt() == CLASS_MAGIC) { bytes = process(bytes); } else { if (verbose) { log("ignoring " + entry.toString()); } } } final ZipEntry outEntry = new ZipEntry(entry.getName()); outEntry.setMethod(entry.getMethod()); outEntry.setComment(entry.getComment()); outEntry.setSize(bytes.length); if (outEntry.getMethod() == ZipEntry.STORED) { final CRC32 crc = new CRC32(); crc.update(bytes); outEntry.setCrc(crc.getValue()); outEntry.setCompressedSize(bytes.length); } out.putNextEntry(outEntry); out.write(bytes); out.closeEntry(); zip.closeEntry(); } out.close(); } finally { fout.close(); } } finally { zip.close(); } if (file.delete()) { final File newFile = new File(tempFile.getAbsolutePath()); if (!newFile.renameTo(file)) { throw new IOException("can not rename " + tempFile + " to " + file); } } else { throw new IOException("can not delete " + file); } } finally { tempFile.delete(); } }