Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit 54d0c16

Browse files
committed
Fix jar hashing and comparison of the Updater on Windows
By comparing a full hash of the jar we are also hashing the build timestamp, meaning that the check always resulted in that the jar is different even though the content didn't change. By hashing all entries in the jar we avoid this, because they don't include any unique build timestamps.
1 parent 5d79cdb commit 54d0c16

File tree

1 file changed

+23
-7
lines changed

1 file changed

+23
-7
lines changed

updater/src/main/scala/Updater.scala

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import java.io.{BufferedInputStream, File, FileInputStream}
1+
import java.io.File
22
import java.net.{URL, URLClassLoader}
33
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
44
import java.nio.file.{FileSystemException, Files, Paths}
55
import java.text.DecimalFormat
66
import java.util.Date
7+
import java.util.jar.JarFile
78
import java.util.zip.ZipFile
89

910
import CLI._
@@ -14,6 +15,7 @@ import org.json4s.{DefaultFormats, Formats}
1415

1516
import scala.collection.JavaConverters._
1617
import scala.io.{Source, StdIn}
18+
import scala.util.hashing.MurmurHash3
1719

1820
/**
1921
* Contains all logic to update the local installation of ChatOverflow.
@@ -215,12 +217,13 @@ object Updater {
215217
// Updater couldn't be updated, because Windows holds file locks on executing files including the updater.
216218
// Skip update of it, it shouldn't change anyway. We can update it on *nix system in the case we reeeeealy need to.
217219
// If it has changed and we can't auto-update, we do recommend the user to update the updater manually, but it is to the user to decide.
218-
val currentIs = new BufferedInputStream(new FileInputStream(s"${conf.directory}/ChatOverflow.jar"))
219-
val currentHash = Stream.continually(currentIs.read).takeWhile(_ != -1).map(_.toByte).hashCode()
220-
val newIs = new BufferedInputStream(zip.getInputStream(entry))
221-
val newHash = Stream.continually(newIs.read).takeWhile(_ != -1).map(_.toByte).hashCode()
220+
val currentJar = new JarFile(s"${conf.directory}/ChatOverflow.jar")
222221

223-
if (currentHash != newHash) {
222+
val tempNewJar = File.createTempFile("ChatOverflow-Updater", "")
223+
Files.write(tempNewJar.toPath, is.readAllBytes())
224+
val newJar = new JarFile(tempNewJar)
225+
226+
if (hashJar(currentJar) != hashJar(newJar)) {
224227
println("The ChatOverflow updater has been updated and we can't update it for you when running on Windows.\n" +
225228
"It's highly recommended to override the 'ChatOverflow.jar' of your installation with the new version\n" +
226229
s"that can be found in the zip file at $zipFile.\n ChatOverflow may still work fine with this version," +
@@ -232,6 +235,8 @@ object Updater {
232235
is.close()
233236
})
234237

238+
zip.close()
239+
235240
// Re-set the executable flag for *nix systems
236241
new File(conf.directory).listFiles()
237242
.filter(f => f.isFile && f.getName.startsWith("ChatOverflow."))
@@ -244,7 +249,18 @@ object Updater {
244249
}
245250

246251
/**
247-
* Gets the local installed version, e.g. 0.3-prealpha.
252+
* Hashes contents of the jar and doesn't include any timestamps,
253+
* because they would be included in a hash of the full jar but doesn't say anything about the content.
254+
*/
255+
def hashJar(jar: JarFile): Int = {
256+
jar.entries().asScala
257+
.map(entry => jar.getInputStream(entry))
258+
.map(is => is.readAllBytes())
259+
.map(arr => MurmurHash3.arrayHash(arr)).sum
260+
}
261+
262+
/**
263+
* Gets the local installed version, e.g. 3.0.0-prealpha.
248264
*
249265
* @return if successfully the version, otherwise None.
250266
*/

0 commit comments

Comments
 (0)