net.lyonlancer5.mcmp.karasu.util.ModFileUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.lyonlancer5.mcmp.karasu.util.ModFileUtils.java

Source

/***************************************************************************\
* Copyright 2016 [Lyonlancer5]                                              *
*                                                                           *
* 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 net.lyonlancer5.mcmp.karasu.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.apache.commons.codec.digest.DigestUtils;
import net.minecraftforge.common.config.Configuration;

public class ModFileUtils {

    private static final ModFileUtils instance = new ModFileUtils();
    private static final Logger LOGGER = LogManager.getLogger("Project Karasu ~ Utils");

    public static boolean doDisplayNewDebug;

    static volatile boolean isDevEnv = false;

    private final File remoteHashes;

    public boolean doUpdateCheck;

    boolean checkForPreReleases;

    private boolean hasInitialized;
    private boolean doHashCheck;

    private ModFileUtils() {
        remoteHashes = new File(Constants.MAIN_DIR, "hash");

        doUpdateCheck = true;
        checkForPreReleases = false;
        hasInitialized = false;
        doHashCheck = true;
    }

    public static ModFileUtils instance() {
        return instance;
    }

    private void initialize() {
        LOGGER.info("Initializing mod utilities");

        //MARKER: Load configuration
        Configuration config = new Configuration(new File(Constants.MAIN_DIR, "config.properties"));
        try {
            config.load();
            config.addCustomCategoryComment("General",
                    "These determine which functions of this mod are enabled or not");
            doDisplayNewDebug = config.getBoolean("Use Minecraft 1.8+ Debug Overlay", "General", true,
                    "Uses the almost-complete debug overlay found in Minecraft 1.8+");

            config.addCustomCategoryComment("Utilities",
                    "These determine which technical add-ons of this mod are used");
            doHashCheck = config.getBoolean("Utilities", "Perform Integrity Check", true,
                    "Performs an integrity check of this mod's files to preserve "
                            + "integrity. The checks performed are MD5 and SHA-1 " + "hash comparisons");
            doUpdateCheck = config.getBoolean("Utilities", "Do Update Checks", true,
                    "Checks for updates by querying the main repository for "
                            + "information regarding new releases.");
            checkForPreReleases = config.getBoolean("Utilities", "Include Pre-release Versions", false,
                    "Checks for pre-release builds along with the stable release checks. This "
                            + "option does not enable checking for development/snapshot builds. "
                            + "This option is ignored if performing update checks are disabled.");
        } catch (Exception e) {
            Constants.LOGGER.warn("Mod configuration failed", e);
        } finally {
            config.save();
        }

        //MARKER: Grab a copy of the hash files
        try {
            if (remoteHashes.exists()) {
                remoteHashes.delete();
            }

            remoteHashes.getParentFile().mkdirs();
            remoteHashes.createNewFile();
            remoteHashes.deleteOnExit();

            download("https://raw.githubusercontent.com/Lyonlancer5/Project-Karasu/master/hash", remoteHashes);
            LOGGER.info("Retrieved remote hash cache");
        } catch (Exception e) {
            LOGGER.warn("Failed to retrieve hash cache");
            doHashCheck = false;
            return;
        }

        hasInitialized = true;
    }

    public void doHashCheck(File jarFile) {
        if (!hasInitialized)
            initialize();

        if (doHashCheck) {
            LOGGER.info("Mod source file located at " + jarFile.getPath());
            if (jarFile.isFile() && !jarFile.getName().endsWith("bin")) {
                try {
                    List<String> lines = IOUtils.readLines(new FileInputStream(remoteHashes));
                    for (String s : lines) {
                        //version:jarName:md5:sha1:sha256:sha512
                        String[] params = s.split(":");
                        if (params[0].equals(Constants.VERSION)) {
                            if (!params[1].equals(jarFile.getName()))
                                LOGGER.warn("JAR filename has been changed!");

                            FileInputStream fis = new FileInputStream(jarFile);
                            String var0 = DigestUtils.md5Hex(fis);
                            String var1 = DigestUtils.sha1Hex(fis);
                            String var2 = DigestUtils.sha256Hex(fis);
                            String var3 = DigestUtils.sha512Hex(fis);

                            assert ((params[2].equals(var0)) && (params[3].equals(var1)) && (params[4].equals(var2))
                                    && (params[5].equals(var3))) : new ValidationError(
                                            "Mod integrity check FAILED: mismatched hashes (Has the mod file been edited?)");

                            LOGGER.info("Validation success!");
                            return;
                        }
                    }
                } catch (IOException e) {
                    throw new ValidationError("Validation FAILED - I/O error", e);
                }
            } else {
                isDevEnv = true;
                LOGGER.warn(
                        "The mod is currently running on a development environment - Integrity checking will not proceed");
            }
        } else {
            LOGGER.warn("#########################################################################");
            LOGGER.warn("WARNING: Integrity checks have been DISABLED!");
            LOGGER.warn("Hash checks will not be performed - this mod may not run as intended");
            LOGGER.warn("Any changes made to this mod will not be validated, whether it came from");
            LOGGER.warn("a legitimate source or an attempt to insert code into this modification");
            LOGGER.warn("#########################################################################");
        }
    }

    static long download(String url, File output) throws IOException {
        URL url1 = new URL(url);
        ReadableByteChannel rbc = Channels.newChannel(url1.openStream());
        FileOutputStream fos = new FileOutputStream(output);

        if (!output.exists()) {
            output.getParentFile().mkdirs();
            output.createNewFile();
        }

        long f = fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        fos.close();
        return f;
    }

    public static class ValidationError extends Error {

        private static final long serialVersionUID = -4092923822851832743L;

        public ValidationError() {
            super();
        }

        public ValidationError(String message) {
            super(message);
        }

        public ValidationError(String message, Throwable cause) {
            super(message, cause);
        }

        public ValidationError(Throwable cause) {
            super(cause);
        }

    }
}