lyonlancer5.karasu.util.ModFileUtils.java Source code

Java tutorial

Introduction

Here is the source code for lyonlancer5.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 lyonlancer5.karasu.util;

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

import net.minecraftforge.common.config.Configuration;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ho.yaml.Yaml;

import lyonlancer5.karasu.LL5_Karasu;

public class ModFileUtils {

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

    private final File yamlConfig;
    private final File remoteHashes;

    private boolean hasInitialized = false;

    private boolean doHashCheck;
    public boolean doUpdateCheck, checkForPreReleases;

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

    public static ModFileUtils getInstance() {
        return instance;
    }

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

        //MARKER: Load configuration (VERY early stage)
        Configuration config = new Configuration(new File(Constants.MAIN_DIR, "config.properties"));
        try {
            config.load();
            doHashCheck = config
                    .get("General", "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")
                    .getBoolean();
            doUpdateCheck = config.get("General", "Do Update Checks", true,
                    "Checks for updates by querying the main repository for "
                            + "information regarding new releases.")
                    .getBoolean();
            checkForPreReleases = config
                    .get("General", "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.")
                    .getBoolean();
        } catch (Exception e) {
            Constants.LOGGER.warn("Mod configuration failed", e);
        } finally {
            config.save();
        }

        //MARKER: Now write settings for the YAML reader
        try {
            if (!yamlConfig.exists()) {
                yamlConfig.createNewFile();

                try {
                    download("https://raw.githubusercontent.com/Lyonlancer5/Project-Karasu/master/jyaml.yml",
                            yamlConfig);
                } catch (Exception e) {
                    try {
                        InputStream in = LL5_Karasu.class.getResourceAsStream("/assets/ll5_karasu/jyaml.yml");
                        PrintWriter writer = new PrintWriter(yamlConfig);
                        for (String s : IOUtils.readLines(in))
                            writer.print(s);
                        writer.flush();
                        writer.close();
                    } catch (Exception e1) {
                        PrintWriter writer = new PrintWriter(yamlConfig);
                        writer.println("minimalOutput: false");
                        writer.println("# Indentation per line would only be two spaces");
                        writer.println("indentAmount: \"  \"");
                        writer.println("suppressWarnings: false");
                        writer.println("encoding: \"UTF-8\"");
                        writer.println("transfers:");
                        writer.println("  keymap: java.util.HashMap");
                        writer.flush();
                        writer.close();
                    }
                }
                LOGGER.info("Created default YAML parser configuration");
            }
        } catch (Exception e) {
            return;
        }

        //MARKER: Then 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.yml", remoteHashes);
            LOGGER.info("Retrieved remote hash cache");
        } catch (Exception e) {
            return;
        }

        hasInitialized = true;
    }

    /*
     * Notes:
     * 
     * Maybe instead of using an outdated YAML parser to check for our files, why don't
     * we use something simpler like reading each line of the hash file and comparing EACH instead?
     * 
     * Or study how the JSON library works :3
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public synchronized void doHashCheck(File jarFile) {
        if (doHashCheck) {
            if (hasInitialized) {
                LOGGER.info("Mod source file " + jarFile.getName() + " located at " + jarFile.getParent());
                if (jarFile.isFile() && !jarFile.getName().endsWith("bin")) {
                    try {
                        HashMap params = (HashMap) ((HashMap) Yaml.loadType(remoteHashes, HashMap.class)
                                .get("version")).get(Constants.VERSION);
                        if (!params.get("jar").equals(jarFile.getName())) {
                            LOGGER.warn("JAR filename has been changed");

                        }
                        FileInputStream fis = new FileInputStream(jarFile);
                        HashMap<String, String> theHashes = (HashMap<String, String>) params.get("hash");
                        String md5 = DigestUtils.md5Hex(fis);
                        String sha1 = DigestUtils.sha1Hex(fis);
                        fis.close();

                        if (md5.equals(theHashes.get("md5"))) {
                            LOGGER.info("Validated MD5 hash - " + md5);
                        } else {
                            throw new RuntimeException(
                                    "MD5 check FAILED: Expected " + md5 + " - Received " + theHashes.get("md5"));
                        }

                        if (sha1.equals(theHashes.get("sha1"))) {
                            LOGGER.info("Validated SHA1 hash - " + sha1);
                        } else {
                            throw new RuntimeException(
                                    "SHA1 check FAILED: Expected " + sha1 + " - Received " + theHashes.get("sha1"));
                        }

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

    static void 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();
        }

        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        fos.close();
    }

}