001package net.tnemc.plugincore.core.module; 002 003import net.tnemc.plugincore.PluginCore; 004import net.tnemc.plugincore.core.utils.IOUtil; 005import org.w3c.dom.Document; 006import org.w3c.dom.Element; 007import org.w3c.dom.Node; 008import org.w3c.dom.NodeList; 009 010import javax.net.ssl.HttpsURLConnection; 011import javax.net.ssl.SSLContext; 012import javax.xml.parsers.DocumentBuilder; 013import javax.xml.parsers.DocumentBuilderFactory; 014import java.io.File; 015import java.io.FileOutputStream; 016import java.io.InputStream; 017import java.net.HttpURLConnection; 018import java.net.URL; 019import java.security.SecureRandom; 020import java.util.Optional; 021 022/* 023 * The New Plugin Core 024 * Copyright (C) 2022 - 2024 Daniel "creatorfromhell" Vidmar 025 * 026 * This program is free software: you can redistribute it and/or modify 027 * it under the terms of the GNU Affero General Public License as published by 028 * the Free Software Foundation, either version 3 of the License, or 029 * (at your option) any later version. 030 * 031 * This program is distributed in the hope that it will be useful, 032 * but WITHOUT ANY WARRANTY; without even the implied warranty of 033 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 034 * GNU Affero General Public License for more details. 035 * 036 * You should have received a copy of the GNU Affero General Public License 037 * along with this program. If not, see <http://www.gnu.org/licenses/>. 038 */ 039public class ModuleUpdateChecker { 040 041 private String module; 042 private String updateURL; 043 private String oldVersion; 044 private String current = ""; 045 private String jarURL = ""; 046 047 public ModuleUpdateChecker(String module, String updateURL, String oldVersion) { 048 this.module = module; 049 this.updateURL = updateURL; 050 this.oldVersion = oldVersion; 051 } 052 public void check() { 053 PluginCore.log().inform("Checking module for update: " + module); 054 if(readInformation()) { 055 if(!upToDate()) { 056 PluginCore.log().inform("Updating module: " + module); 057 if(download(module, jarURL)) { 058 PluginCore.log().inform("Downloaded module update for " + module); 059 } else { 060 PluginCore.log().inform("Failed to download module update for " + module); 061 } 062 PluginCore.loader().load(module); 063 } else { 064 PluginCore.log().inform("ModuleOld " + module + " is up to date."); 065 } 066 } 067 } 068 069 public boolean upToDate() { 070 if(current.trim().equalsIgnoreCase("")) { 071 return true; 072 } 073 074 String[] oldSplit = oldVersion.split("\\."); 075 String[] currentSplit = current.split("\\."); 076 077 for(int i = 0; i < currentSplit.length; i++) { 078 079 if(i >= oldSplit.length && !currentSplit[i].equalsIgnoreCase("0")) return false; 080 if(i >= oldSplit.length && currentSplit[i].equalsIgnoreCase("0")) continue; 081 082 if(Integer.parseInt(currentSplit[i]) > Integer.parseInt(oldSplit[i])) return false; 083 } 084 return true; 085 } 086 087 public static boolean download(String module, String jarURL) { 088 if(jarURL.trim().equalsIgnoreCase("")) { 089 return false; 090 } 091 092 if(PluginCore.loader().hasModule(module)) { 093 PluginCore.loader().unload(module); 094 } 095 096 try { 097 098 SSLContext sc = SSLContext.getInstance("TLS"); 099 sc.init(null, IOUtil.selfCertificates(), new SecureRandom()); 100 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 101 102 final URL url = new URL(jarURL); 103 final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 104 final int responseCode = connection.getResponseCode(); 105 if (responseCode == HttpURLConnection.HTTP_OK) { 106 String fileName = jarURL.substring(jarURL.lastIndexOf("/") + 1); 107 108 try(InputStream in = connection.getInputStream()) { 109 final File file = new File(PluginCore.directory() + File.separator + "modules", fileName); 110 111 if(file.exists()) { 112 if(!file.renameTo(new File(PluginCore.directory() + File.separator + "modules", "outdated-" + fileName))) { 113 return false; 114 } 115 } 116 117 try(FileOutputStream out = new FileOutputStream(file)) { 118 119 int bytesRead = -1; 120 final byte[] buffer = new byte[4096]; 121 while((bytesRead = in.read(buffer)) != -1) { 122 out.write(buffer, 0, bytesRead); 123 } 124 125 out.close(); 126 in.close(); 127 return true; 128 } 129 } 130 } 131 } catch (Exception ignore) { 132 return false; 133 } 134 return false; 135 } 136 137 public boolean readInformation() { 138 final Optional<Document> document = readUpdateURL(updateURL); 139 if(document.isPresent()) { 140 final Document doc = document.get(); 141 142 final NodeList mainNodes = doc.getElementsByTagName("modules"); 143 if(mainNodes != null && mainNodes.getLength() > 0) { 144 final Node modulesNode = mainNodes.item(0); 145 final Element element = (Element)modulesNode; 146 147 final NodeList modules = element.getElementsByTagName("module"); 148 149 for(int i = 0; i < modules.getLength(); i++) { 150 final Node moduleNode = modules.item(i); 151 152 if(moduleNode.hasAttributes()) { 153 154 final Node nameNode = moduleNode.getAttributes().getNamedItem("name"); 155 if (nameNode != null) { 156 157 if (nameNode.getTextContent().equalsIgnoreCase(module)) { 158 159 final Node releasedNode = moduleNode.getAttributes().getNamedItem("released"); 160 if (releasedNode != null) { 161 if(releasedNode.getTextContent().equalsIgnoreCase("yes")) { 162 163 //We have the correct name, and this module is released. 164 final Element moduleElement = (Element)moduleNode; 165 166 final NodeList versions = moduleElement.getElementsByTagName("versions"); 167 168 if(versions.getLength() > 0) { 169 170 final NodeList versionsNodes = moduleElement.getElementsByTagName("version"); 171 172 for(int v = 0; v < versionsNodes.getLength(); v++) { 173 174 final Node versionNode = versionsNodes.item(v); 175 if(versionNode != null && versionNode.hasAttributes()) { 176 177 final Element versionElement = (Element)versionNode; 178 179 final Node latest = versionNode.getAttributes().getNamedItem("latest"); 180 final Node versionReleased = versionNode.getAttributes().getNamedItem("released"); 181 182 if(latest != null && latest.getTextContent().equalsIgnoreCase("yes") && 183 versionReleased != null && versionReleased.getTextContent().equalsIgnoreCase("yes")) { 184 185 //We have the latest module version 186 final NodeList name = versionElement.getElementsByTagName("name"); 187 final NodeList jar = versionElement.getElementsByTagName("jar"); 188 189 if(name.getLength() > 0) { 190 this.current = name.item(0).getTextContent(); 191 } 192 193 if(jar.getLength() > 0) { 194 this.jarURL = jar.item(0).getTextContent(); 195 } 196 return true; 197 } 198 } 199 } 200 201 } 202 203 } 204 205 } 206 } 207 } 208 } 209 } 210 } 211 } 212 return false; 213 } 214 215 public static Optional<Document> readUpdateURL(String updateURL) { 216 try { 217 218 SSLContext sc = SSLContext.getInstance("TLS"); 219 sc.init(null, IOUtil.selfCertificates(), new SecureRandom()); 220 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 221 222 final URL url = new URL(updateURL); 223 final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 224 225 final DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); 226 final DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder(); 227 final Document document = documentBuilder.parse(connection.getInputStream()); 228 229 return Optional.of(document); 230 231 } catch(Exception e) { 232 return Optional.empty(); 233 } 234 } 235 236 public String getModule() { 237 return module; 238 } 239 240 public void setModule(String module) { 241 this.module = module; 242 } 243 244 public String getURL() { 245 return updateURL; 246 } 247 248 public void setURL(String updateURL) { 249 this.updateURL = updateURL; 250 } 251 252 public String getOldVersion() { 253 return oldVersion; 254 } 255 256 public void setOldVersion(String oldVersion) { 257 this.oldVersion = oldVersion; 258 } 259 260 public String getCurrent() { 261 return current; 262 } 263 264 public void setCurrent(String current) { 265 this.current = current; 266 } 267 268 public String getJarURL() { 269 return jarURL; 270 } 271 272 public void setJarURL(String jarURL) { 273 this.jarURL = jarURL; 274 } 275}