All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.java.truevfs.comp.zipdriver.AbstractZipDriver Maven / Gradle / Ivy

/*
 * Copyright (C) 2005-2015 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package net.java.truevfs.comp.zipdriver;

import edu.umd.cs.findbugs.annotations.CreatesObligation;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Objects;
import java.util.zip.Deflater;
import javax.annotation.CheckForNull;
import javax.annotation.WillNotClose;
import javax.annotation.concurrent.Immutable;
import net.java.truecommons.cio.*;
import static net.java.truecommons.cio.Entry.Access.WRITE;
import static net.java.truecommons.cio.Entry.Size.DATA;
import net.java.truecommons.cio.Entry.Type;
import static net.java.truecommons.cio.Entry.Type.DIRECTORY;
import net.java.truecommons.key.spec.KeyManagerMap;
import net.java.truecommons.key.spec.KeyProvider;
import net.java.truecommons.key.spec.sl.KeyManagerMapLocator;
import net.java.truecommons.logging.LocalizedLogger;
import net.java.truecommons.shed.BitField;
import net.java.truecommons.shed.HashMaps;
import net.java.truevfs.comp.zip.ZipCryptoParameters;
import net.java.truevfs.comp.zip.ZipEntry;
import static net.java.truevfs.comp.zip.ZipEntry.*;
import net.java.truevfs.comp.zip.ZipFileParameters;
import net.java.truevfs.comp.zip.ZipOutputStreamParameters;
import net.java.truevfs.kernel.spec.*;
import static net.java.truevfs.kernel.spec.FsAccessOption.*;
import net.java.truevfs.kernel.spec.cio.MultiplexingOutputService;
import net.java.truevfs.kernel.spec.sl.IoBufferPoolLocator;
import org.slf4j.Logger;

/**
 * An abstract archive driver for the ZIP file format.
 * 

* Sub-classes must be thread-safe and should be immutable! * * @param the type of the ZIP driver entries. * @author Christian Schlichtherle */ @Immutable public abstract class AbstractZipDriver extends FsArchiveDriver implements ZipOutputStreamParameters, ZipFileParameters { private static final Logger logger = new LocalizedLogger(AbstractZipDriver.class); /** * {@inheritDoc} *

* The implementation in the class {@link AbstractZipDriver} returns * {@code IoBufferPoolLocator.SINGLETON.get()}. */ @Override public IoBufferPool getPool() { return IoBufferPoolLocator.SINGLETON.get(); } /** * Returns the map of key managers for accessing protected resources * (encryption). *

* This is an immutable property - multiple calls must return the same * object. * * @return {@link KeyManagerMapLocator#SINGLETON}, as by the implementation * in the class {@link ZipDriver}. */ public KeyManagerMap getKeyManagerMap() { return KeyManagerMapLocator.SINGLETON; } public final @CheckForNull ZipCryptoParameters zipCryptoParameters(ZipInputService input) { return zipCryptoParameters(input.getModel(), input.getRawCharset()); } public final @CheckForNull ZipCryptoParameters zipCryptoParameters(ZipOutputService output) { return zipCryptoParameters(output.getModel(), output.getRawCharset()); } /** * Returns the ZIP crypto parameters for the given file system model * and character set or {@code null} if not available. * To enable the use of this method when writing an archive entry with the * client APIs, you must use {@link FsAccessOption#ENCRYPT}. *

* The implementation in the class {@link ZipDriver} returns * {@code new KeyManagerZipCryptoParameters(this, model, charset)}. * * @param model the file system model. * @param charset charset the character set used for encoding entry names * and the file comment in the ZIP file. * @return The ZIP crypto parameters for the given file system model * and character set or {@code null} if not available. */ protected @CheckForNull ZipCryptoParameters zipCryptoParameters( FsModel model, Charset charset) { return new KeyManagerZipCryptoParameters(this, model, charset); } /** * A template method for resolving the resource URI which is required to * look up the {@link KeyProvider} for the mount point of the file system * with the given model. *

* The implementation in the class {@link ZipDriver} returns the * expression {@code model.getMountPoint().toHierarchicalUri()} * in order to improve the readability of the URI in comparison to the * expression {@code model.getMountPoint().getUri()}. * * @param model the file system model. * @return The URI which represents the file system model's mount point. * @see #TRUEZIP-72 */ public URI mountPointUri(FsModel model) { return model.getMountPoint().toHierarchicalUri(); } /** * A template method for resolving the resource URI which is required to * look up the {@link KeyProvider} for the entry with the given name in the * file system with the given model. *

* The implementation in the class {@code ZipDriver} ignores the given * entry name and just returns the expression {@code mountPointUri(model)} * in order to lookup the same key provider for all entries in a ZIP file. *

* An alternative implementation in a sub-class could return the expression * {@code mountPointUri(model).resolve("/" + name)} instead. * * @param model the file system model. * @param name the entry name. * @return The URI for looking up a {@link KeyProvider}. */ public URI fileSystemUri(FsModel model, String name) { //return mountPointUri(model).resolve("/" + name); return mountPointUri(model); } /** * {@inheritDoc} * * @return The implementation in the class {@link ZipDriver} returns * {@code true} because when reading a ZIP file sequentially, * each ZIP entry should "override" any previously read * ZIP entry with an equal name. * This holds true even if the central directory is used to access * the ZIP entries in random order. */ @Override public boolean getRedundantContentSupport() { return true; } /** * {@inheritDoc} * * @return The implementation in the class {@link ZipDriver} returns * {@code true} because when reading a ZIP file sequentially, * each ZIP entry should "override" any previously read * ZIP entry with an equal name. * This holds true even if the central directory is used to access * the ZIP entries in random order. */ @Override public boolean getRedundantMetaDataSupport() { return true; } /** * Whether or not the content of the given entry shall get * checked/authenticated when reading it. * If this method returns {@code true} and the check fails, then an * {@link IOException} gets thrown. * * @param local the entry to test. * @param input the origin of the entry. * @return {@code entry.isEncrypted()}. */ public boolean check( E local, @WillNotClose ZipInputService input) { return local.isEncrypted(); } public final boolean rdc( @WillNotClose ZipInputService input, E local, AbstractZipDriverEntry peer) { return rdc(local, peer); } public final boolean rdc( @WillNotClose ZipOutputService output, E local, AbstractZipDriverEntry peer) { return rdc(peer, local); } /** * Returns {@code true} if and only if the content of the given input * target entry is eligible for Raw Data Copying (RDC). * This method gets called twice (once on each side of a copy operation) * and should return {@code false} unless both target entries can mutually * agree on transferring raw (unprocessed) content. * Note that it is an error to compare the properties of the target entries * because this method may get called before the output target * entry gets mutated to compare equal with the input target entry! *

* The implementation in the class {@link ZipDriver} returns * {@code !local.isEncrypted() && !remote.isEncrypted()} in order to cover * the typical case that the cipher keys of both targets are not the same. * Note that there is no safe way to explicitly test for this. * * @param input the input target entry for copying the contents. * @param output the output target entry for copying the contents. * @return Whether the content to get copied from the input target entry * to the output target entry is eligible for Raw Data Copying * (RDC). */ protected boolean rdc(AbstractZipDriverEntry input, AbstractZipDriverEntry output) { return !input.isEncrypted() && !output.isEncrypted(); } /** * {@inheritDoc} *

* The implementation in the class {@link ZipDriver} * returns {@code false}. * * @return {@code false} */ @Override public boolean getPreambled() { return false; } /** * {@inheritDoc} *

* The implementation in the class {@link ZipDriver} * returns {@code false}. * * @return {@code false} */ @Override public boolean getPostambled() { return false; } /** * {@inheritDoc} *

* The implementation in the class {@link ZipDriver} * returns {@code Maps#OVERHEAD_SIZE}. * * @return {@code Maps#OVERHEAD_SIZE} */ @Override public int getOverheadSize() { return HashMaps.OVERHEAD_SIZE; } /** * {@inheritDoc} *

* The implementation in the class {@link ZipDriver} * returns {@code ZipEntry#DEFLATED}. * * @return {@code ZipEntry#DEFLATED} */ @Override public int getMethod() { return DEFLATED; } /** * {@inheritDoc} *

* The implementation in the class {@link ZipDriver} * returns {@code Deflater#BEST_COMPRESSION}. * * @return {@code Deflater#BEST_COMPRESSION} */ @Override public int getLevel() { return Deflater.BEST_COMPRESSION; } /** * {@inheritDoc} *

* The implementation in the class {@link ZipDriver} decorates the * given controller with a package private controller which keeps track of * the AES PBE parameters. * This should pool overridden in order to return just {@code controller} if * and only if you are overriding * {@link #zipCryptoParameters(FsModel, Charset)}, too, and do not want to * use the locatable key manager to resolve passwords, e.g. for WinZip AES * encryption. */ @Override public FsController decorate(FsController controller) { return new ZipKeyController(controller, this); } @Override protected final ZipInputService newInput( final FsModel model, final FsInputSocketSource source) throws IOException { final ZipInputService zis = newZipInput(Objects.requireNonNull(model), source); try { zis.recoverLostEntries(); } catch (final IOException ex) { logger.warn("junkInTheTrunk.warn", mountPointUri(model), zis.getPostambleLength()); logger.trace("junkInTheTrunk.trace", ex); } return zis; } @CreatesObligation protected ZipInputService newZipInput( FsModel model, FsInputSocketSource source) throws IOException { assert null != model; return new ZipInputService<>(model, source, this); } @Override @CreatesObligation protected OutputService newOutput( FsModel model, FsOutputSocketSink sink, final @CheckForNull @WillNotClose InputService input) throws IOException { final ZipInputService zis = (ZipInputService) input; return new MultiplexingOutputService<>(getPool(), new ZipOutputService<>(model, sink, zis, this)); } /** * This implementation modifies {@code options} in the following way before * it forwards the call to {@code controller}: *

    *
  1. {@link FsAccessOption#STORE} is set. *
  2. If {@link FsAccessOption#GROW} is set, then * {@link FsAccessOption#APPEND} gets set, too, and * {@link FsAccessOption#CACHE} gets cleared. *
*

* The resulting output socket is then wrapped in a private nested class * for an upcast in {@link #newOutput}. * Thus, when overriding this method, {@link #newOutput} should get * overridden, too. * Otherwise, a class cast exception will pool thrown in * {@link #newOutput}. */ @Override protected FsOutputSocketSink sink( BitField options, final FsController controller, final FsNodeName name) { // Leave FsAccessOption.COMPRESS untouched - the driver shall be given // opportunity to get its own preferences to sort out such a conflict. options = options.set(STORE); if (options.get(GROW)) options = options.set(APPEND).clear(CACHE); return new FsOutputSocketSink(options, controller.output(options, name, null)); } @Override public E newEntry( final BitField options, String name, final Type type, final @CheckForNull Entry template) { name = normalize(name, type); final E entry; if (template instanceof ZipEntry) { entry = newEntry(name, (ZipEntry) template); } else { entry = newEntry(name); if (null != template) { entry.setTime(template.getTime(WRITE)); entry.setSize(template.getSize(DATA)); } } if (DIRECTORY != type) { if (UNKNOWN == entry.getMethod()) { final int method; if (options.get(COMPRESS)) method = DEFLATED; else if (options.get(STORE)) method = STORED; else method = getMethod(); entry.setMethod(method); if (STORED != method) entry.setCompressedSize(UNKNOWN); } if (options.get(ENCRYPT)) entry.setEncrypted(true); } return entry; } /** * Returns a new ZIP driver entry with the given {@code name}. * * @param name the entry name. * @return A new abstract ZIP driver entry. */ @Override public abstract E newEntry(String name); /** * Returns a new ZIP driver entry with the given {@code name} and all * other properties copied from the given template. * * @param name the entry name. * @param template the template entry. * @return A new ZIP driver entry. */ public abstract E newEntry(String name, ZipEntry template); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy