diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index 31b0b93f..0cfca57c 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -39,6 +39,7 @@ import com.muwire.core.search.ResultsEvent import com.muwire.core.search.ResultsSender import com.muwire.core.search.SearchEvent import com.muwire.core.search.SearchManager +import com.muwire.core.search.UIResultBatchEvent import com.muwire.core.trust.TrustEvent import com.muwire.core.trust.TrustService import com.muwire.core.update.UpdateClient @@ -233,7 +234,9 @@ public class Core { cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000) log.info("initializing update client") - updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props) + updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props, fileManager, me) + eventBus.register(FileDownloadedEvent.class, updateClient) + eventBus.register(UIResultBatchEvent.class, updateClient) log.info("initializing connector") I2PConnector i2pConnector = new I2PConnector(socketManager) diff --git a/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy b/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy index 1f2966d2..50ac7d09 100644 --- a/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy +++ b/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy @@ -13,6 +13,7 @@ class MuWireSettings { boolean allowUntrusted int downloadRetryInterval int updateCheckInterval + boolean autoDownloadUpdate String nickname File downloadLocation CrawlerResponse crawlerResponse @@ -37,6 +38,7 @@ class MuWireSettings { System.getProperty("user.home"))) downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1")) updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24")) + autoDownloadUpdate = Boolean.parseBoolean(props.getProperty("autoDownloadUpdate","true")) shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true")) downloadSequentialRatio = Float.valueOf(props.getProperty("downloadSequentialRatio","0.8")) hostClearInterval = Integer.valueOf(props.getProperty("hostClearInterval","60")) @@ -62,6 +64,7 @@ class MuWireSettings { props.setProperty("downloadLocation", downloadLocation.getAbsolutePath()) props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval)) props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval)) + props.setProperty("autoDownloadUpdate", String.valueOf(autoDownloadUpdate)) props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles)) props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio)) props.setProperty("hostClearInterval", String.valueOf(hostClearInterval)) diff --git a/core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy b/core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy index 22bc3e30..0c753092 100644 --- a/core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy +++ b/core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy @@ -3,7 +3,15 @@ package com.muwire.core.update import java.util.logging.Level import com.muwire.core.EventBus +import com.muwire.core.InfoHash import com.muwire.core.MuWireSettings +import com.muwire.core.Persona +import com.muwire.core.download.UIDownloadEvent +import com.muwire.core.files.FileDownloadedEvent +import com.muwire.core.files.FileManager +import com.muwire.core.search.QueryEvent +import com.muwire.core.search.SearchEvent +import com.muwire.core.search.UIResultBatchEvent import groovy.json.JsonOutput import groovy.json.JsonSlurper @@ -13,6 +21,7 @@ import net.i2p.client.I2PSessionMuxedListener import net.i2p.client.SendMessageOptions import net.i2p.client.datagram.I2PDatagramDissector import net.i2p.client.datagram.I2PDatagramMaker +import net.i2p.data.Base64 import net.i2p.util.VersionComparator @Log @@ -21,16 +30,23 @@ class UpdateClient { final I2PSession session final String myVersion final MuWireSettings settings + final FileManager fileManager + final Persona me private final Timer timer private long lastUpdateCheckTime - UpdateClient(EventBus eventBus, I2PSession session, String myVersion, MuWireSettings settings) { + private volatile InfoHash updateInfoHash + private volatile boolean updateDownloading + + UpdateClient(EventBus eventBus, I2PSession session, String myVersion, MuWireSettings settings, FileManager fileManager, Persona me) { this.eventBus = eventBus this.session = session this.myVersion = myVersion this.settings = settings + this.fileManager = fileManager + this.me = me timer = new Timer("update-client",true) } @@ -43,6 +59,24 @@ class UpdateClient { timer.cancel() } + void onUIResultBatchEvent(UIResultBatchEvent results) { + if (results.results[0].infohash != updateInfoHash) + return + if (updateDownloading) + return + updateDownloading = true + def file = new File(settings.downloadLocation, results.results[0].name) + def downloadEvent = new UIDownloadEvent(result: results.results[0], sources : results.results[0].sources, target : file) + eventBus.publish(downloadEvent) + } + + void onFileDownloadedEvent(FileDownloadedEvent e) { + if (e.downloadedFile.infoHash != updateInfoHash) + return + updateDownloading = false + eventBus.publish(new UpdateDownloadedEvent(version : payload.version, signer : payload.signer)) + } + private void checkUpdate() { final long now = System.currentTimeMillis() if (lastUpdateCheckTime > 0) { @@ -105,9 +139,24 @@ class UpdateClient { log.info("no new version available") return } - - log.info("new version $payload.version available, publishing event") - eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer, infoHash : payload.infoHash)) + + if (!settings.autoDownloadUpdate) { + log.info("new version $payload.version available, publishing event") + eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer, infoHash : payload.infoHash)) + } else { + log.info("new version $payload.version available") + updateInfoHash = new InfoHash(Base64.decode($payload.infoHash)) + if (fileManager.rootToFiles.containsKey(updateInfoHash)) + eventBus.publish(new UpdateDownloadedEvent(version : payload.version, signer : payload.signer)) + else { + updateDownloading = false + log.info("starting search for new version hash $payload.infoHash") + def searchEvent = new SearchEvent(searchHash : updateInfoHash.getRoot(), uuid : UUID.randomUUID(), oobInfohash : true) + def queryEvent = new QueryEvent(searchEvent : searchEvent, firstHop : true, replyTo : me.destination, + receivedOn : me.destination, originator : me) + eventBus.publish(queryEvent) + } + } } catch (Exception e) { log.log(Level.WARNING,"Invalid datagram",e) diff --git a/core/src/main/groovy/com/muwire/core/update/UpdateDownloadedEvent.groovy b/core/src/main/groovy/com/muwire/core/update/UpdateDownloadedEvent.groovy new file mode 100644 index 00000000..aae9ca08 --- /dev/null +++ b/core/src/main/groovy/com/muwire/core/update/UpdateDownloadedEvent.groovy @@ -0,0 +1,8 @@ +package com.muwire.core.update + +import com.muwire.core.Event + +class UpdateDownloadedEvent extends Event { + String version + String signer +} diff --git a/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy b/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy index 7c8863c6..16cebf09 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy @@ -70,6 +70,10 @@ class OptionsController { model.updateCheckInterval = text settings.updateCheckInterval = Integer.valueOf(text) + boolean autoDownloadUpdate = view.autoDownloadUpdateCheckbox.model.isSelected() + model.autoDownloadUpdate = autoDownloadUpdate + settings.autoDownloadUpdate = autoDownloadUpdate + boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected() model.onlyTrusted = onlyTrusted settings.setAllowUntrusted(!onlyTrusted) diff --git a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy index b8e3319b..dc84604d 100644 --- a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy @@ -29,6 +29,7 @@ import com.muwire.core.search.UIResultEvent import com.muwire.core.trust.TrustEvent import com.muwire.core.trust.TrustService import com.muwire.core.update.UpdateAvailableEvent +import com.muwire.core.update.UpdateDownloadedEvent import com.muwire.core.upload.UploadEvent import com.muwire.core.upload.UploadFinishedEvent @@ -143,6 +144,7 @@ class MainFrameModel { core.eventBus.register(FileUnsharedEvent.class, this) core.eventBus.register(RouterDisconnectedEvent.class, this) core.eventBus.register(AllFilesLoadedEvent.class, this) + core.eventBus.register(UpdateDownloadedEvent.class, this) timer.schedule({ if (core.shutdown.get()) @@ -185,6 +187,14 @@ class MainFrameModel { watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) } } } + + void onUpdateDownloadedEvent(UpdateDownloadedEvent e) { + runInsideUIAsync { + JOptionPane.showMessageDialog(null, "MuWire $e.version has been downloaded. You can update now", + "Update Downloaded", JOptionPane.INFORMATION_MESSAGE) + } + } + void onUIResultEvent(UIResultEvent e) { MVCGroup resultsGroup = results.get(e.uuid) resultsGroup?.model.handleResult(e) diff --git a/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy b/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy index 843fe2cf..fed0b572 100644 --- a/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy @@ -11,6 +11,7 @@ import griffon.metadata.ArtifactProviderFor class OptionsModel { @Observable String downloadRetryInterval @Observable String updateCheckInterval + @Observable boolean autoDownloadUpdate @Observable boolean onlyTrusted @Observable boolean shareDownloadedFiles @Observable String downloadLocation @@ -40,6 +41,7 @@ class OptionsModel { MuWireSettings settings = application.context.get("muwire-settings") downloadRetryInterval = settings.downloadRetryInterval updateCheckInterval = settings.updateCheckInterval + autoDownloadUpdate = settings.autoDownloadUpdate onlyTrusted = !settings.allowUntrusted() shareDownloadedFiles = settings.shareDownloadedFiles downloadLocation = settings.downloadLocation.getAbsolutePath() diff --git a/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy b/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy index 2773416b..8222f5b4 100644 --- a/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy @@ -32,6 +32,7 @@ class OptionsView { def retryField def updateField + def autoDownloadUpdateCheckbox def allowUntrustedCheckbox def shareDownloadedCheckbox @@ -71,16 +72,19 @@ class OptionsView { label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1)) updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1)) label(text : "hours", constraints : gbc(gridx: 2, gridy : 1)) + + label(text : "Download updates automatically", constraints: gbc(gridx :0, gridy : 2)) + autoDownloadUpdateCheckbox = checkBox(selected : bind {model.autoDownloadUpdate}, constraints : gbc(gridx:1, gridy : 2)) - label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 2)) - allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 2)) + label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 3)) + allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 3)) - label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3)) - shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3)) + label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:4)) + shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:4)) - label(text : "Save downloaded files to:", constraints: gbc(gridx:0, gridy:4)) - button(text : "Choose", constraints : gbc(gridx : 1, gridy:4), downloadLocationAction) - label(text : bind {model.downloadLocation}, constraints: gbc(gridx:0, gridy:5, gridwidth:2)) + label(text : "Save downloaded files to:", constraints: gbc(gridx:0, gridy:5)) + button(text : "Choose", constraints : gbc(gridx : 1, gridy:5), downloadLocationAction) + label(text : bind {model.downloadLocation}, constraints: gbc(gridx:0, gridy:6, gridwidth:2)) } i = builder.panel {