/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.tool;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import divconq.lang.op.FuncResult;
import divconq.lang.op.OperationResult;
import divconq.struct.CompositeParser;
import divconq.struct.CompositeStruct;
import divconq.struct.ListStruct;
import divconq.struct.RecordStruct;
import divconq.struct.Struct;
import divconq.util.HashUtil;
import divconq.util.IOUtil;
import divconq.util.StringUtil;
public class Updater {
public static Path DEPLOYED_PATH = Paths.get("./config/deployed.json");
public static void main(String[] args) {
boolean updated = Updater.tryUpdate();
while (updated) {
System.out.println();
System.out.println("Checking for additional updates");
System.out.println();
updated = Updater.tryUpdate();
}
System.out.println("Updates completed!");
}
static public FuncResult<RecordStruct> loadDeployed() {
FuncResult<RecordStruct> res = new FuncResult<>();
FuncResult<CompositeStruct> pres = CompositeParser.parseJson(DEPLOYED_PATH);
if (!pres.hasErrors())
res.setResult((RecordStruct) pres.getResult());
return res;
}
static public OperationResult saveDeployed(RecordStruct deployed) {
OperationResult res = new OperationResult();
IOUtil.saveEntireFile(Updater.DEPLOYED_PATH, deployed.toPrettyString());
return res;
}
static public boolean tryUpdate() {
@SuppressWarnings("resource")
final Scanner scan = new Scanner(System.in);
FuncResult<RecordStruct> ldres = Updater.loadDeployed();
if (ldres.hasErrors()) {
System.out.println("Error reading deployed.json file: " + ldres.getMessage());
return false;
}
RecordStruct deployed = ldres.getResult();
String ver = deployed.getFieldAsString("Version");
String packfolder = deployed.getFieldAsString("PackageFolder");
String packprefix = deployed.getFieldAsString("PackagePrefix");
if (StringUtil.isEmpty(ver) || StringUtil.isEmpty(packfolder)) {
System.out.println("Error reading deployed.json file: Missing Version or PackageFolder");
return false;
}
if (StringUtil.isEmpty(packprefix))
packprefix = "DivConq";
System.out.println("Current Version: " + ver);
Path packpath = Paths.get(packfolder);
if (!Files.exists(packpath) || !Files.isDirectory(packpath)) {
System.out.println("Error reading PackageFolder - it may not exist or is not a folder.");
return false;
}
File pp = packpath.toFile();
RecordStruct deployment = null;
File matchpack = null;
for (File f : pp.listFiles()) {
if (!f.getName().startsWith(packprefix + "-") || !f.getName().endsWith("-bin.zip"))
continue;
System.out.println("Checking: " + f.getName());
// if not a match before, clear this
deployment = null;
try {
ZipFile zf = new ZipFile(f);
Enumeration<ZipArchiveEntry> entries = zf.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
if (entry.getName().equals("deployment.json")) {
//System.out.println("crc: " + entry.getCrc());
FuncResult<CompositeStruct> pres = CompositeParser.parseJson(zf.getInputStream(entry));
if (pres.hasErrors()) {
System.out.println("Error reading deployment.json file");
break;
}
deployment = (RecordStruct) pres.getResult();
break;
}
}
zf.close();
}
catch (IOException x) {
System.out.println("Error reading deployment.json file: " + x);
}
if (deployment != null) {
String fndver = deployment.getFieldAsString("Version");
String fnddependson = deployment.getFieldAsString("DependsOn");
if (ver.equals(fnddependson)) {
System.out.println("Found update: " + fndver);
matchpack = f;
break;
}
}
}
if ((matchpack == null) || (deployment == null)) {
System.out.println("No updates found!");
return false;
}
String fndver = deployment.getFieldAsString("Version");
String umsg = deployment.getFieldAsString("UpdateMessage");
if (StringUtil.isNotEmpty(umsg)) {
System.out.println("========================================================================");
System.out.println(umsg);
System.out.println("========================================================================");
}
System.out.println();
System.out.println("Do you want to install? (y/n)");
System.out.println();
String p = scan.nextLine().toLowerCase();
if (!p.equals("y"))
return false;
System.out.println();
System.out.println("Intalling: " + fndver);
Set<String> ignorepaths = new HashSet<>();
ListStruct iplist = deployment.getFieldAsList("IgnorePaths");
if (iplist != null) {
for (Struct df : iplist.getItems())
ignorepaths.add(df.toString());
}
ListStruct dflist = deployment.getFieldAsList("DeleteFiles");
// deleting
if (dflist != null) {
for (Struct df : dflist.getItems()) {
Path delpath = Paths.get(".", df.toString());
if (Files.exists(delpath)) {
System.out.println("Deleting: " + delpath.toAbsolutePath());
try {
Files.delete(delpath);
}
catch (IOException x) {
System.out.println("Unable to Delete: " + x);
}
}
}
}
// copying updates
System.out.println("Checking for updated files: ");
try {
@SuppressWarnings("resource")
ZipFile zf = new ZipFile(matchpack);
Enumeration<ZipArchiveEntry> entries = zf.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
String entryname = entry.getName().replace('\\', '/');
boolean xfnd = false;
for (String exculde : ignorepaths)
if (entryname.startsWith(exculde)) {
xfnd = true;
break;
}
if (xfnd)
continue;
System.out.print(".");
Path localpath = Paths.get(".", entryname);
if (entry.isDirectory()) {
if (!Files.exists(localpath))
Files.createDirectories(localpath);
}
else {
boolean hashmatch = false;
if (Files.exists(localpath)) {
String local = null;
String update = null;
try (InputStream lin = Files.newInputStream(localpath)) {
local = HashUtil.getMd5(lin);
}
try (InputStream uin = zf.getInputStream(entry)) {
update = HashUtil.getMd5(uin);
}
hashmatch = (StringUtil.isNotEmpty(local) && StringUtil.isNotEmpty(update) && local.equals(update));
}
if (!hashmatch) {
System.out.print("[" + entryname + "]");
try (InputStream uin = zf.getInputStream(entry)) {
Files.createDirectories(localpath.getParent());
Files.copy(uin, localpath, StandardCopyOption.REPLACE_EXISTING);
}
catch (Exception x) {
System.out.println("Error updating: " + entryname + " - " + x);
return false;
}
}
}
}
zf.close();
}
catch (IOException x) {
System.out.println("Error reading update package: " + x);
}
// updating local config
deployed.setField("Version", fndver);
OperationResult svres = Updater.saveDeployed(deployed);
if (svres.hasErrors()) {
System.out.println("Intalled: " + fndver + " but could not update deployed.json. Repair the file before continuing.\nError: " + svres.getMessage());
return false;
}
System.out.println("Intalled: " + fndver);
return true;
}
}