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

asic4j.ContainerFile Maven / Gradle / Ivy

Go to download

Java library for generating CDOC 1.0/1.1/2.0 files with AES-256 GCM and RSA or ECC recipients

There is a newer version: 0.0.5
Show newest version
package asic4j;

import org.apache.commons.io.IOUtils;

import java.io.*;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class ContainerFile implements AutoCloseable {

    private final FileSystem fs;
    private final ZipFile zf;
    private String mimetype;
    private Manifest manifest;
    private Set meta = new HashSet<>();  // Files that reside inside the ZIP in META-INF, thus are not part of the payload

    private ContainerFile(FileSystem fs, ZipFile zf) {
        this.fs = fs;
        this.zf = zf;
    }

    public static ContainerFile open(File f) throws IOException {
        return open(f.toPath());
    }

    public static ContainerFile open(Path p) throws IOException {
        // TODO: file internally
        URI uri = URI.create("jar:" + p.toUri());
        //System.out.println("Opening " + uri);
        Map env = new HashMap<>();
        FileSystem fs = FileSystems.newFileSystem(uri, env);
        return new ContainerFile(fs, new ZipFile(p.toFile()));
    }

    public static ContainerFile create(Path p, String mimetype) throws IOException {
        return new ContainerFile(null, new ZipFile(p.toFile()));
    }

    public static String identify(File f) throws IOException {
        try (InputStream in = new FileInputStream(f)) {
            byte[] header = new byte[100];
            if (in.read(header, 0, 38) > 38) {
                System.out.println("PK: " + new String(header, 0, 2, StandardCharsets.US_ASCII.name()));
                System.out.println("mimetype: " + new String(header, 30, 8, StandardCharsets.US_ASCII.name()));
                //System.out.println("mimetype: " + HexUtils.bin2hex(Arrays.copyOfRange(header, 38, 60)));
            }
        }
        return null;
    }

    public Manifest getManifest() {
        return manifest;
    }

    public Set getMetaFiles() {
        return meta;
    }

    public boolean check(List errors) throws IOException {
        // SHOULD have comment
        if (zf.getComment() == null) {
            errors.add("Warning: No ZIP comment with MIME!");
        }

        // MUST have mimetype
        ZipEntry mimetype = zf.getEntry("mimetype");
        if (mimetype == null) {
            errors.add("Error: No mimetype file!");
            return false;
        }
        // Check for STORED
        if (mimetype.getMethod() != ZipEntry.STORED) {
            errors.add("Warning: mimetype is not STORED!");
        }
        // Check for extras
        if (mimetype.getExtra() != null) {
            errors.add("Warning: mimetype has extras!");
        }

        // Read mimetype entry
        try (BufferedReader bin = new BufferedReader(new InputStreamReader(zf.getInputStream(mimetype), StandardCharsets.US_ASCII))) {
            this.mimetype = bin.readLine();
            //debug.println("Info: mimetype=" + this.mimetype);
        }

        ZipEntry manifest = zf.getEntry("META-INF/manifest.xml");
        // Must have manifest
        if (manifest == null) {
            errors.add("Error: No manifest.xml!");
            return false;
        }

        // Check if manifest matches entries
        try (InputStream manifestin = zf.getInputStream(manifest)) {
            this.manifest = Manifest.fromStream(manifestin, this.mimetype);
        }

        // Check ZIP content
        Set zip_entries = new HashSet<>();

        // Validate structure of zip
        for (ZipEntry e : Collections.list(zf.entries())) {
            if (e.getComment() != null) {
                //debug.println("Comment=" + e.getComment());
            }
            if (e.getExtra() != null) {
                //debug.println("Extra=" + HexUtils.bin2hex(e.getExtra()));
            }
            // OpenDocument 3.2. These should not be in manifest.xml XXX
            if (e.getName().equals("mimetype")) {
                continue;
            }
            if (e.isDirectory()) {
                //debug.println("Warning: folder entry in ZIP: " + e.getName());
                continue;
            }
            if (e.getName().startsWith("META-INF/")) {
                // manifest.xml is handled internally
                if (e.getName().equals("META-INF/manifest.xml")) {
                    continue;
                }
                // Other files are tracked
                meta.add(e.getName());
                continue;
            } else {
                // Payload, must be in manifest.xml
                zip_entries.add(e.getName());
            }
        }

        // Validate that payload files match.
        HashSet mf_entries = new HashSet<>();
        for (ManifestEntry mfile : this.manifest.getFiles()) {
            mf_entries.add(mfile.path);
        }
        mf_entries.retainAll(zip_entries);
        if (mf_entries.size() != zip_entries.size()) {
            //debug.println("Error: Manifest does not match ZIP content." + mf_entries.size());
        }
        return true;
    }

    /**
     * Get a file from the container with the given path.
     *
     * @param name path of the file in zip container
     * @return bytes of the file
     * @throws IOException
     */
    public byte[] get(String name) throws IOException {
        ZipEntry ze = zf.getEntry(name);
        try (InputStream fin = zf.getInputStream(ze)) {
            return IOUtils.toByteArray(fin);
        }
    }

    public InputStream getStream(String name) throws IOException {
        return Files.newInputStream(fs.getPath(name));
    }

    public String getMimeType() {
        return mimetype;
    }

    @Override
    public void close() throws IOException {
        fs.close();
        zf.close();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy