2 * Sone - UpdateChecker.java - Copyright © 2011–2019 David Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.sone.core
20 import com.google.common.eventbus.*
21 import com.google.common.primitives.*
22 import com.google.inject.Inject
24 import net.pterodactylus.sone.core.event.*
25 import net.pterodactylus.sone.main.*
26 import net.pterodactylus.sone.utils.*
27 import net.pterodactylus.util.version.Version
30 import java.util.logging.*
31 import java.util.logging.Logger.*
32 import javax.inject.Singleton
35 * Watches the official Sone homepage for new releases.
38 class UpdateChecker @Inject constructor(
39 private val eventBus: EventBus,
40 private val freenetInterface: FreenetInterface,
41 private val currentRunningVersion: Version,
42 pluginHomepage: PluginHomepage) {
44 private val logger: Logger = getLogger(UpdateChecker::class.java.name)
46 private val currentUri by lazy { FreenetURI(pluginHomepage.homepage) }
48 var latestEdition = SonePlugin.getLatestEdition()
51 var latestVersion: Version = currentRunningVersion
54 var latestVersionDate: Long = 0
57 fun hasLatestVersion() =
58 latestVersion > currentRunningVersion
61 freenetInterface.registerUsk(currentUri) { uri, edition, newKnownGood, newSlot ->
62 logger.log(Level.FINEST, String.format("Found update for %s: %d, %s, %s", uri, edition, newKnownGood, newSlot))
63 if (newKnownGood || newSlot) {
65 freenetInterface.fetchUri(uri.setMetaString(arrayOf("sone.properties")))
67 logger.log(Level.WARNING, String.format("Could not fetch properties of latest homepage: %s", uri))
69 ?.asBucket()?.use { resultBucket ->
70 resultBucket.inputStream
71 .let { parseProperties(it) }
72 .let { extractCurrentVersion(it) }
73 .onNull { logger.log(Level.INFO, "Invalid data parsed from properties.") }
74 ?.takeIf { it.version > latestVersion }
75 ?.also { updateVersionInformation(it, edition) }
76 ?.also { logger.info { "Found new version: %s (%tc%s)".format(it.version, it.time, if (it.disruptive) ", disruptive" else "") } }
77 ?.also { eventBus.post(UpdateFoundEvent(it.version, it.time, edition, it.disruptive)) }
79 } catch (ioe1: IOException) {
80 logger.log(Level.WARNING, String.format("Could not parse sone.properties of %s!", uri), ioe1)
87 freenetInterface.unregisterUsk(currentUri)
90 private fun updateVersionInformation(versionInformation: VersionInformation, edition: Long) {
91 latestVersion = versionInformation.version
92 latestVersionDate = versionInformation.time
93 latestEdition = edition
96 private fun parseProperties(propertiesInputStream: InputStream) =
98 InputStreamReader(propertiesInputStream, "UTF-8").use { inputStreamReader ->
99 load(inputStreamReader)
103 private fun extractCurrentVersion(properties: Properties) =
104 properties.getProperty("CurrentVersion/Version")
105 ?.let { Version.parse(it) }
107 properties.getProperty("CurrentVersion/ReleaseTime")
108 ?.let { Longs.tryParse(it) }
110 VersionInformation(version, time, disruptiveVersionBetweenCurrentAndFound(properties))
114 private fun disruptiveVersionBetweenCurrentAndFound(properties: Properties) =
115 properties.stringPropertyNames()
116 .filter { it.startsWith("DisruptiveVersion/") }
117 .map { it.removePrefix("DisruptiveVersion/") }
118 .map { Version.parse(it) }
119 .any { it > currentRunningVersion }
123 private data class VersionInformation(val version: Version, val time: Long, val disruptive: Boolean)