GordianCoreZipFileEntry.java

/*
 * GordianKnot: Security Suite
 * Copyright 2012-2026. Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package io.github.tonywasher.joceanus.gordianknot.impl.core.zip;

import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.zip.GordianZipFileContents;
import io.github.tonywasher.joceanus.gordianknot.api.zip.GordianZipFileEntry;
import io.github.tonywasher.joceanus.gordianknot.impl.core.stream.GordianStreamDefinition;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;

/**
 * Class represents an encrypted file in the Zip file.
 */
public class GordianCoreZipFileEntry
        implements GordianZipFileEntry {
    /**
     * The NonHeader Error text.
     */
    private static final String ERROR_NONHEADER = "Entry is not a header";

    /**
     * The User property prefix.
     */
    private static final String PROP_USERPFIX = "User";

    /**
     * The Name property of a file.
     */
    private static final String PROP_NAME = "Name";

    /**
     * The Zip Name property of a file.
     */
    private static final String PROP_ZIPNAME = "ZipName";

    /**
     * The Hash property name of a file.
     */
    private static final String PROP_HASH = "Hash";

    /**
     * The Header property name of a file.
     */
    private static final String PROP_HEADER = "Header";

    /**
     * The Stream Type property.
     */
    private static final String PROP_TYPE = "StreamType";

    /**
     * The Stream Vector property.
     */
    private static final String PROP_VECTOR = "StreamVector";

    /**
     * The Stream Value property.
     */
    private static final String PROP_VALUE = "StreamValue";

    /**
     * The parent contents.
     */
    private GordianZipFileContents theParent;

    /**
     * The properties.
     */
    private final GordianZipFileProperties theProperties;

    /**
     * The file name.
     */
    private final String theFileName;

    /**
     * The zip name.
     */
    private String theZipName;

    /**
     * The original file size.
     */
    private Long theFileSize;

    /**
     * The compressed file size.
     */
    private Long theCompressedSize;

    /**
     * The Hash.
     */
    private byte[] theHash;

    /**
     * Is this the header.
     */
    private boolean isHeader;

    /**
     * Standard constructor from filename.
     *
     * @param pFileName the file name
     */
    GordianCoreZipFileEntry(final String pFileName) {
        /* Store the file name */
        theFileName = pFileName;

        /* Allocate the properties */
        theProperties = new GordianZipFileProperties();
    }

    /**
     * Standard constructor from properties.
     *
     * @param pProperties the properties
     */
    GordianCoreZipFileEntry(final GordianZipFileProperties pProperties) {
        /* Store the properties */
        theProperties = pProperties;

        /* Access the top-level details */
        theFileName = pProperties.getStringProperty(PROP_NAME);
        theZipName = pProperties.getStringProperty(PROP_ZIPNAME);

        /* Determine whether this is a header */
        isHeader = pProperties.getLongProperty(PROP_HEADER) != null;

        /* If this is the header */
        if (isHeader) {
            /* Set hash keys */
            theHash = pProperties.getByteProperty(PROP_HASH);

            /* Else standard entry */
        } else {
            /* Access file sizes */
            theFileSize = pProperties.getLongProperty(PROP_NAME);
            theCompressedSize = pProperties.getLongProperty(PROP_ZIPNAME);
        }
    }

    @Override
    public String getFileName() {
        return theFileName;
    }

    /**
     * Obtain the parent contents.
     *
     * @return the parent of the fileEntry
     */
    public GordianZipFileContents getParent() {
        return theParent;
    }

    /**
     * Obtain the Zip name of the file.
     *
     * @return the Zip name of the file
     */
    public String getZipName() {
        return theZipName;
    }

    /**
     * Obtain the original size of a file.
     *
     * @return the original size
     */
    public Long getSize() {
        return theFileSize;
    }

    /**
     * Obtain the compressed size of a file.
     *
     * @return the compressed size
     */
    public Long getCompressedSize() {
        return theCompressedSize;
    }

    /**
     * Obtain the public key.
     *
     * @return the public key
     */
    protected byte[] getHash() {
        return theHash;
    }

    /**
     * Is this entry the header.
     *
     * @return true/false
     */
    protected boolean isHeader() {
        return isHeader;
    }

    /**
     * Set the properties of the file.
     *
     * @return the properties
     */
    protected GordianZipFileProperties allocateProperties() {
        /* Set the top-level details */
        theProperties.setProperty(PROP_NAME, theFileName);
        theProperties.setProperty(PROP_NAME, theFileSize);
        theProperties.setProperty(PROP_ZIPNAME, theZipName);
        theProperties.setProperty(PROP_ZIPNAME, theCompressedSize);

        /* If this is the header */
        if (isHeader) {
            /* Note the header */
            theProperties.setProperty(PROP_HEADER, 1L);

            /* Set private/public keys */
            theProperties.setProperty(PROP_HASH, theHash);
        }

        /* Return the properties */
        return theProperties;
    }

    @Override
    public void setUserStringProperty(final String pPropertyName,
                                      final String pPropertyValue) {
        /* Set the property */
        theProperties.setProperty(PROP_USERPFIX
                + pPropertyName, pPropertyValue);
    }

    @Override
    public void setUserLongProperty(final String pPropertyName,
                                    final Long pPropertyValue) {
        /* Set the property */
        theProperties.setProperty(PROP_USERPFIX
                + pPropertyName, pPropertyValue);
    }

    @Override
    public String getUserStringProperty(final String pPropertyName) {
        /* Set the property */
        return theProperties.getStringProperty(PROP_USERPFIX
                + pPropertyName);
    }

    @Override
    public Long getUserLongProperty(final String pPropertyName) {
        /* Set the property */
        return theProperties.getLongProperty(PROP_USERPFIX
                + pPropertyName);
    }

    /**
     * Set parent.
     *
     * @param pParent the parent
     */
    protected void setParent(final GordianZipFileContents pParent) {
        theParent = pParent;
    }

    /**
     * Set the ZipEntry for the file.
     *
     * @param pEntry the zip entry
     */
    protected void setZipEntry(final ZipEntry pEntry) {
        theZipName = pEntry.getName();
        theFileSize = pEntry.getSize();
        theCompressedSize = pEntry.getCompressedSize();
    }

    /**
     * Set the hash.
     *
     * @param pHash the hash value
     */
    protected void setHash(final byte[] pHash) {
        if (!isHeader) {
            throw new IllegalArgumentException(ERROR_NONHEADER);
        }
        theHash = Arrays.copyOf(pHash, pHash.length);
    }

    /**
     * Set header indication.
     */
    protected void setHeader() {
        isHeader = true;
    }

    /**
     * Build the properties.
     *
     * @param pStreams the stream definitions
     */
    public void buildProperties(final List<GordianStreamDefinition> pStreams) {
        /* Loop through the streams */
        int iIndex = 0;
        final Iterator<GordianStreamDefinition> myIterator = pStreams.iterator();
        while (myIterator.hasNext()) {
            final GordianStreamDefinition myStream = myIterator.next();
            iIndex++;

            /* Set the type property */
            theProperties.setProperty(PROP_TYPE
                    + iIndex, myStream.getExternalId());
            theProperties.setProperty(PROP_TYPE
                    + iIndex, myStream.getTypeDefinition());

            /* Set the initVector property */
            theProperties.setProperty(PROP_VECTOR
                    + iIndex, myStream.getInitVector());

            /* Set the value property */
            theProperties.setProperty(PROP_VALUE
                    + iIndex, myStream.getValue());

            /* Handle extras */
            switch (myStream.getType()) {
                case MAC:
                    theCompressedSize = myStream.getDataLength();
                    break;
                case DIGEST:
                    theFileSize = myStream.getDataLength();
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * Build input list.
     *
     * @return the new input list
     * @throws GordianException on error
     */
    public List<GordianStreamDefinition> buildInputList() throws GordianException {
        /* Create list */
        final List<GordianStreamDefinition> myList = new ArrayList<>();

        /* Loop through the streamProperties */
        int myIndex = 1;
        while (true) {
            /* Check for property and break loop if not found */
            final Long myType = theProperties.getLongProperty(PROP_TYPE
                    + myIndex);
            if (myType == null) {
                break;
            }

            /* Access remaining properties */
            final byte[] myTypeDef = theProperties.getByteProperty(PROP_TYPE
                    + myIndex);
            final byte[] myVector = theProperties.getByteProperty(PROP_VECTOR
                    + myIndex);
            final byte[] myValue = theProperties.getByteProperty(PROP_VALUE
                    + myIndex);

            /* Define the stream */
            myList.add(new GordianStreamDefinition(myType, myTypeDef, myVector, myValue));
            myIndex++;
        }

        /* Return the list */
        return myList;
    }
}