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 049 this.module = module; 050 this.updateURL = updateURL; 051 this.oldVersion = oldVersion; 052 } 053 054 public static boolean download(String module, String jarURL) { 055 056 if(jarURL.trim().equalsIgnoreCase("")) { 057 return false; 058 } 059 060 if(PluginCore.loader().hasModule(module)) { 061 PluginCore.loader().unload(module); 062 } 063 064 try { 065 066 SSLContext sc = SSLContext.getInstance("TLS"); 067 sc.init(null, IOUtil.selfCertificates(), new SecureRandom()); 068 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 069 070 final URL url = new URL(jarURL); 071 final HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); 072 final int responseCode = connection.getResponseCode(); 073 if(responseCode == HttpURLConnection.HTTP_OK) { 074 String fileName = jarURL.substring(jarURL.lastIndexOf("/") + 1); 075 076 try(InputStream in = connection.getInputStream()) { 077 final File file = new File(PluginCore.directory() + File.separator + "modules", fileName); 078 079 if(file.exists()) { 080 if(!file.renameTo(new File(PluginCore.directory() + File.separator + "modules", "outdated-" + fileName))) { 081 return false; 082 } 083 } 084 085 try(FileOutputStream out = new FileOutputStream(file)) { 086 087 int bytesRead = -1; 088 final byte[] buffer = new byte[4096]; 089 while((bytesRead = in.read(buffer)) != -1) { 090 out.write(buffer, 0, bytesRead); 091 } 092 093 out.close(); 094 in.close(); 095 return true; 096 } 097 } 098 } 099 } catch(Exception ignore) { 100 return false; 101 } 102 return false; 103 } 104 105 public static Optional<Document> readUpdateURL(String updateURL) { 106 107 try { 108 109 SSLContext sc = SSLContext.getInstance("TLS"); 110 sc.init(null, IOUtil.selfCertificates(), new SecureRandom()); 111 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 112 113 final URL url = new URL(updateURL); 114 final HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); 115 116 final DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); 117 final DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder(); 118 final Document document = documentBuilder.parse(connection.getInputStream()); 119 120 return Optional.of(document); 121 122 } catch(Exception e) { 123 return Optional.empty(); 124 } 125 } 126 127 public void check() { 128 129 PluginCore.log().inform("Checking module for update: " + module); 130 if(readInformation()) { 131 if(!upToDate()) { 132 PluginCore.log().inform("Updating module: " + module); 133 if(download(module, jarURL)) { 134 PluginCore.log().inform("Downloaded module update for " + module); 135 } else { 136 PluginCore.log().inform("Failed to download module update for " + module); 137 } 138 PluginCore.loader().load(module); 139 } else { 140 PluginCore.log().inform("ModuleOld " + module + " is up to date."); 141 } 142 } 143 } 144 145 public boolean upToDate() { 146 147 if(current.trim().equalsIgnoreCase("")) { 148 return true; 149 } 150 151 String[] oldSplit = oldVersion.split("\\."); 152 String[] currentSplit = current.split("\\."); 153 154 for(int i = 0; i < currentSplit.length; i++) { 155 156 if(i >= oldSplit.length && !currentSplit[i].equalsIgnoreCase("0")) return false; 157 if(i >= oldSplit.length && currentSplit[i].equalsIgnoreCase("0")) continue; 158 159 if(Integer.parseInt(currentSplit[i]) > Integer.parseInt(oldSplit[i])) return false; 160 } 161 return true; 162 } 163 164 public boolean readInformation() { 165 166 final Optional<Document> document = readUpdateURL(updateURL); 167 if(document.isPresent()) { 168 final Document doc = document.get(); 169 170 final NodeList mainNodes = doc.getElementsByTagName("modules"); 171 if(mainNodes != null && mainNodes.getLength() > 0) { 172 final Node modulesNode = mainNodes.item(0); 173 final Element element = (Element)modulesNode; 174 175 final NodeList modules = element.getElementsByTagName("module"); 176 177 for(int i = 0; i < modules.getLength(); i++) { 178 final Node moduleNode = modules.item(i); 179 180 if(moduleNode.hasAttributes()) { 181 182 final Node nameNode = moduleNode.getAttributes().getNamedItem("name"); 183 if(nameNode != null) { 184 185 if(nameNode.getTextContent().equalsIgnoreCase(module)) { 186 187 final Node releasedNode = moduleNode.getAttributes().getNamedItem("released"); 188 if(releasedNode != null) { 189 if(releasedNode.getTextContent().equalsIgnoreCase("yes")) { 190 191 //We have the correct name, and this module is released. 192 final Element moduleElement = (Element)moduleNode; 193 194 final NodeList versions = moduleElement.getElementsByTagName("versions"); 195 196 if(versions.getLength() > 0) { 197 198 final NodeList versionsNodes = moduleElement.getElementsByTagName("version"); 199 200 for(int v = 0; v < versionsNodes.getLength(); v++) { 201 202 final Node versionNode = versionsNodes.item(v); 203 if(versionNode != null && versionNode.hasAttributes()) { 204 205 final Element versionElement = (Element)versionNode; 206 207 final Node latest = versionNode.getAttributes().getNamedItem("latest"); 208 final Node versionReleased = versionNode.getAttributes().getNamedItem("released"); 209 210 if(latest != null && latest.getTextContent().equalsIgnoreCase("yes") && 211 versionReleased != null && versionReleased.getTextContent().equalsIgnoreCase("yes")) { 212 213 //We have the latest module version 214 final NodeList name = versionElement.getElementsByTagName("name"); 215 final NodeList jar = versionElement.getElementsByTagName("jar"); 216 217 if(name.getLength() > 0) { 218 this.current = name.item(0).getTextContent(); 219 } 220 221 if(jar.getLength() > 0) { 222 this.jarURL = jar.item(0).getTextContent(); 223 } 224 return true; 225 } 226 } 227 } 228 229 } 230 231 } 232 233 } 234 } 235 } 236 } 237 } 238 } 239 } 240 return false; 241 } 242 243 public String getModule() { 244 245 return module; 246 } 247 248 public void setModule(String module) { 249 250 this.module = module; 251 } 252 253 public String getURL() { 254 255 return updateURL; 256 } 257 258 public void setURL(String updateURL) { 259 260 this.updateURL = updateURL; 261 } 262 263 public String getOldVersion() { 264 265 return oldVersion; 266 } 267 268 public void setOldVersion(String oldVersion) { 269 270 this.oldVersion = oldVersion; 271 } 272 273 public String getCurrent() { 274 275 return current; 276 } 277 278 public void setCurrent(String current) { 279 280 this.current = current; 281 } 282 283 public String getJarURL() { 284 285 return jarURL; 286 } 287 288 public void setJarURL(String jarURL) { 289 290 this.jarURL = jarURL; 291 } 292}