Handles the request to update the router by firing off an + * {@link net.i2p.util.EepGet} call to download the latest unsigned zip file + * and displaying the status to anyone who asks. + *
+ *After the download completes the signed update file is copied to the + * router directory, and if configured the router is restarted to complete + * the update process. + *
+ */ +class UnsignedUpdateHandler implements Updater { + private final RouterContext _context; + + public UnsignedUpdateHandler(RouterContext ctx) { + _context = ctx; + } + + /** + * @param currentVersion ignored, we use time stored in a property + */ + @Override + public UpdateTask check(UpdateType type, UpdateMethod method, + String id, String currentVersion, long maxTime) { + if (type != UpdateType.ROUTER_UNSIGNED || method != UpdateMethod.HTTP) + return null; + + String url = _context.getProperty(ConfigUpdateHandler.PROP_ZIP_URL); + if (url == null) + return null; + + ListHandles the request to update the router by firing one or more + * {@link net.i2p.util.EepGet} calls to download the latest signed update file + * and displaying the status to anyone who asks. + *
+ *After the download completes the signed update file is verified with + * {@link net.i2p.crypto.TrustedUpdate}, and if it's authentic the payload + * of the signed update file is unpacked and the router is restarted to complete + * the update process. + *
+ * + * This does not do any checking, that is handled by the NewsFetcher. + */ +class UpdateHandler implements Updater { + protected final RouterContext _context; + + public UpdateHandler(RouterContext ctx) { + _context = ctx; + } + + /** Can't check, the NewsHandler does that */ + public UpdateTask check(UpdateType type, UpdateMethod method, + String id, String currentVersion, long maxTime) { + return null; + } + + /** + * Start a download and return a handle to the download task. + * Should not block. + * + * @param id plugin name or ignored + * @param maxTime how long you have + * @return active task or null if unable to download + */ + public UpdateTask update(UpdateType type, UpdateMethod method, List+Classes to implement the update process. +
+ + diff --git a/apps/routerconsole/java/src/net/i2p/router/web/CSSHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/CSSHelper.java index 15c2b2b2c2..785af69a3e 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/CSSHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/CSSHelper.java @@ -59,7 +59,7 @@ public class CSSHelper extends HelperBase { public void setNews(String val) { // Protected with nonce in css.jsi if (val != null) - NewsFetcher.getInstance(_context).showNews(val.equals("1")); + NewsHelper.showNews(_context, val.equals("1")); } /** diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java index 6c37db8290..06f87e377f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java @@ -1,6 +1,8 @@ package net.i2p.router.web; import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -12,6 +14,8 @@ import java.util.Set; import net.i2p.router.client.ClientManagerFacadeImpl; import net.i2p.router.startup.ClientAppConfig; import net.i2p.router.startup.LoadClientAppsJob; +import net.i2p.router.update.ConsoleUpdateManager; +import static net.i2p.update.UpdateType.*; import org.mortbay.jetty.handler.ContextHandlerCollection; @@ -333,7 +337,7 @@ public class ConfigClientsHandler extends FormHandler { /** @since 0.8.13 */ private void updateAllPlugins() { - if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) { + if (NewsHelper.isAnyUpdateInProgress()) { addFormError(_("Plugin or update download already in progress.")); return; } @@ -346,17 +350,26 @@ public class ConfigClientsHandler extends FormHandler { } private void installPlugin(String url) { - if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) { + ConsoleUpdateManager mgr = (ConsoleUpdateManager) _context.updateManager(); + if (mgr == null) { + addFormError("Update manager not registered, cannot install"); + return; + } + if (mgr.isUpdateInProgress()) { addFormError(_("Plugin or update download already in progress.")); return; } - PluginUpdateHandler puh = PluginUpdateHandler.getInstance(_context); - if (puh.isRunning()) { - addFormError(_("Plugin or update download already in progress.")); + URI uri; + try { + uri = new URI(url); + } catch (URISyntaxException use) { + addFormError(_("Bad URL {0}", url)); return; } - puh.update(url); - addFormNotice(_("Downloading plugin from {0}", url)); + if (mgr.installPlugin(uri)) + addFormNotice(_("Downloading plugin from {0}", url)); + else + addFormError("Cannot install, check logs"); // So that update() will post a status to the summary bar before we reload try { Thread.sleep(1000); @@ -364,16 +377,12 @@ public class ConfigClientsHandler extends FormHandler { } private void checkPlugin(String app) { - if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) { - addFormError(_("Plugin or update download already in progress.")); + ConsoleUpdateManager mgr = (ConsoleUpdateManager) _context.updateManager(); + if (mgr == null) { + addFormError("Update manager not registered, cannot check"); return; } - PluginUpdateChecker puc = PluginUpdateChecker.getInstance(_context); - if (puc.isRunning()) { - addFormError(_("Plugin or update download already in progress.")); - return; - } - puc.update(app); + mgr.check(PLUGIN, app); addFormNotice(_("Checking plugin {0} for updates", app)); // So that update() will post a status to the summary bar before we reload try { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java index c381657015..b4a1155ffc 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java @@ -293,7 +293,7 @@ public class ConfigClientsHelper extends HelperBase { * Like in DataHelper but doesn't convert null to "" * There's a lot worse things a plugin could do but... */ - static String stripHTML(Properties props, String key) { + public static String stripHTML(Properties props, String key) { String orig = props.getProperty(key); if (orig == null) return null; String t1 = orig.replace('<', ' '); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java index a3b30d337a..0340dde3e7 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java @@ -6,6 +6,8 @@ import java.util.Map; import net.i2p.I2PAppContext; import net.i2p.crypto.TrustedUpdate; import net.i2p.data.DataHelper; +import net.i2p.router.update.ConsoleUpdateManager; +import static net.i2p.update.UpdateType.*; import net.i2p.util.FileUtil; import net.i2p.util.PortMapper; @@ -84,7 +86,7 @@ public class ConfigUpdateHandler extends FormHandler { * @return the configured value, else the registered HTTP proxy, else the default * @since 0.8.13 */ - static int proxyPort(I2PAppContext ctx) { + public static int proxyPort(I2PAppContext ctx) { return ctx.getProperty(PROP_PROXY_PORT, ctx.portMapper().getPort(PortMapper.SVC_HTTP_PROXY, DEFAULT_PROXY_PORT_INT)); } @@ -94,11 +96,16 @@ public class ConfigUpdateHandler extends FormHandler { if (_action == null) return; if (_action.equals(_("Check for updates"))) { - NewsFetcher fetcher = NewsFetcher.getInstance(_context); - fetcher.fetchNews(); - if (fetcher.shouldFetchUnsigned()) - fetcher.fetchUnsignedHead(); - if (fetcher.updateAvailable() || fetcher.unsignedUpdateAvailable()) { + ConsoleUpdateManager mgr = (ConsoleUpdateManager) _context.updateManager(); + if (mgr == null) { + addFormError("Update manager not registered, cannot check"); + return; + } + boolean a1 = mgr.checkAvailable(NEWS, 60*1000) != null; + boolean a2 = false; + if ((!a1) && _updateUnsigned && _zipURL != null && _zipURL.length() > 0) + a2 = mgr.checkAvailable(ROUTER_UNSIGNED, 60*1000) != null; + if (a1 || a2) { if ( (_updatePolicy == null) || (!_updatePolicy.equals("notify")) ) addFormNotice(_("Update available, attempting to download now")); else @@ -118,7 +125,8 @@ public class ConfigUpdateHandler extends FormHandler { String oldURL = ConfigUpdateHelper.getNewsURL(_context); if ( (oldURL == null) || (!_newsURL.equals(oldURL)) ) { changes.put(PROP_NEWS_URL, _newsURL); - NewsFetcher.getInstance(_context).invalidateNews(); + // this invalidates the news + changes.put(NewsHelper.PROP_LAST_CHECKED, "0"); addFormNotice(_("Updating news URL to {0}", _newsURL)); } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java index 07fad31975..7fecfc85f5 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java @@ -14,7 +14,7 @@ public class ConfigUpdateHelper extends HelperBase { @Override public void setContextId(String contextId) { super.setContextId(contextId); - _dontInstall = NewsFetcher.getInstance(_context).dontInstall(); + _dontInstall = NewsHelper.dontInstall(_context); } public boolean canInstall() { @@ -159,6 +159,6 @@ public class ConfigUpdateHelper extends HelperBase { } public String getNewsStatus() { - return NewsFetcher.getInstance(_context).status(); + return NewsHelper.status(_context); } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java index 7bb1e99d05..41ffec627b 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java @@ -60,7 +60,7 @@ public class FileDumpHelper extends HelperBase { dumpDir(buf, dir, ".war"); // plugins - File pluginDir = new File(_context.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR); + File pluginDir = new File(_context.getConfigDir(), PluginStarter.PLUGIN_DIR); File[] files = pluginDir.listFiles(); if (files != null) { Arrays.sort(files); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java index de9d427eea..c2878b383c 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java @@ -20,7 +20,7 @@ public class LogsHelper extends HelperBase { } /** @since 0.8.13 */ - static String jettyVersion() { + public static String jettyVersion() { return Server.getVersion(); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java deleted file mode 100644 index 84b5a75ee5..0000000000 --- a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java +++ /dev/null @@ -1,416 +0,0 @@ -package net.i2p.router.web; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; - -import net.i2p.crypto.TrustedUpdate; -import net.i2p.data.DataHelper; -import net.i2p.router.Router; -import net.i2p.router.RouterContext; -import net.i2p.router.RouterVersion; -import net.i2p.router.util.RFC822Date; -import net.i2p.util.EepGet; -import net.i2p.util.EepHead; -import net.i2p.util.FileUtil; -import net.i2p.util.Log; - -/** - * Task to periodically look for updates to the news.xml, and to keep - * track of whether that has an announcement for a new version. - */ -public class NewsFetcher implements Runnable, EepGet.StatusListener { - private final RouterContext _context; - private final Log _log; - private boolean _updateAvailable; - private boolean _unsignedUpdateAvailable; - private long _lastFetch; - private long _lastUpdated; - private String _updateVersion; - private String _unsignedUpdateVersion; - private String _lastModified; - private boolean _invalidated; - private final File _newsFile; - private final File _tempFile; - private static NewsFetcher _instance; - private volatile boolean _isRunning; - - //public static final synchronized NewsFetcher getInstance() { return _instance; } - public static final synchronized NewsFetcher getInstance(RouterContext ctx) { - if (_instance != null) - return _instance; - _instance = new NewsFetcher(ctx); - return _instance; - } - - private static final String NEWS_FILE = "docs/news.xml"; - private static final String TEMP_NEWS_FILE = "news.xml.temp"; - /** @since 0.7.14 not configurable */ - private static final String BACKUP_NEWS_URL = "http://www.i2p2.i2p/_static/news/news.xml"; - private static final String PROP_LAST_CHECKED = "router.newsLastChecked"; - /** @since 0.8.12 */ - private static final String PROP_LAST_HIDDEN = "routerconsole.newsLastHidden"; - - private NewsFetcher(RouterContext ctx) { - _context = ctx; - _log = ctx.logManager().getLog(NewsFetcher.class); - _instance = this; - try { - String last = ctx.getProperty(PROP_LAST_CHECKED); - if (last != null) - _lastFetch = Long.parseLong(last); - } catch (NumberFormatException nfe) {} - _newsFile = new File(_context.getRouterDir(), NEWS_FILE); - _tempFile = new File(_context.getTempDir(), TEMP_NEWS_FILE); - updateLastFetched(); - _updateVersion = ""; - _isRunning = true; - } - - /** @since 0.8.8 */ - void shutdown() { - _isRunning = false; - } - - private void updateLastFetched() { - if (_newsFile.exists()) { - if (_lastUpdated == 0) - _lastUpdated = _newsFile.lastModified(); - if (_lastFetch == 0) - _lastFetch = _lastUpdated; - if (_lastModified == null) - _lastModified = RFC822Date.to822Date(_lastFetch); - } else { - _lastUpdated = 0; - _lastFetch = 0; - _lastModified = null; - } - } - - public boolean updateAvailable() { return _updateAvailable; } - public String updateVersion() { return _updateVersion; } - public boolean unsignedUpdateAvailable() { return _unsignedUpdateAvailable; } - public String unsignedUpdateVersion() { return _unsignedUpdateVersion; } - - /** - * Is the news newer than the last time it was hidden? - * @since 0.8.12 - */ - public boolean shouldShowNews() { - if (_lastUpdated <= 0) - return true; - String h = _context.getProperty(PROP_LAST_HIDDEN); - if (h == null) - return true; - long last = 0; - try { - last = Long.parseLong(h); - } catch (NumberFormatException nfe) {} - return _lastUpdated > last; - } - - /** - * Save config with the timestamp of the current news to hide, or 0 to show - * @since 0.8.12 - */ - public void showNews(boolean yes) { - long stamp = yes ? 0 : _lastUpdated; - _context.router().saveConfig(PROP_LAST_HIDDEN, Long.toString(stamp)); - } - - /** - * @return HTML - */ - public String status() { - StringBuilder buf = new StringBuilder(128); - long now = _context.clock().now(); - buf.append(""); - if (_lastUpdated > 0) { - buf.append(Messages.getString("News last updated {0} ago.", - DataHelper.formatDuration2(now - _lastUpdated), - _context)) - .append('\n'); - } - if (_lastFetch > _lastUpdated) { - buf.append(Messages.getString("News last checked {0} ago.", - DataHelper.formatDuration2(now - _lastFetch), - _context)); - } - buf.append(""); - String consoleNonce = System.getProperty("router.consoleNonce"); - if (_lastUpdated > 0 && consoleNonce != null) { - if (shouldShowNews()) { - buf.append(" ") - .append(Messages.getString("Hide news", _context)); - } else { - buf.append(" ") - .append(Messages.getString("Show news", _context)); - } - buf.append(""); - } - return buf.toString(); - } - - private static final long INITIAL_DELAY = 5*60*1000; - private static final long RUN_DELAY = 10*60*1000; - - public void run() { - try { Thread.sleep(INITIAL_DELAY + _context.random().nextLong(INITIAL_DELAY)); } catch (InterruptedException ie) {} - while (_isRunning) { - if (!_updateAvailable) checkForUpdates(); - if (shouldFetchNews()) { - fetchNews(); - if (shouldFetchUnsigned()) - fetchUnsignedHead(); - } - try { Thread.sleep(RUN_DELAY); } catch (InterruptedException ie) {} - } - } - - boolean dontInstall() { - File test = new File(_context.getBaseDir(), "history.txt"); - boolean readonly = ((test.exists() && !test.canWrite()) || (!_context.getBaseDir().canWrite())); - boolean disabled = _context.getBooleanProperty(ConfigUpdateHandler.PROP_UPDATE_DISABLED); - return readonly || disabled; - } - - private boolean shouldInstall() { - String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY); - if ("notify".equals(policy) || dontInstall()) - return false; - File zip = new File(_context.getRouterDir(), Router.UPDATE_FILE); - return !zip.exists(); - } - - private boolean shouldFetchNews() { - if (_invalidated) - return true; - updateLastFetched(); - String freq = _context.getProperty(ConfigUpdateHandler.PROP_REFRESH_FREQUENCY, - ConfigUpdateHandler.DEFAULT_REFRESH_FREQUENCY); - try { - long ms = Long.parseLong(freq); - if (ms <= 0) - return false; - - if (_lastFetch + ms < _context.clock().now()) { - return true; - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Last fetched " + DataHelper.formatDuration(_context.clock().now() - _lastFetch) + " ago"); - return false; - } - } catch (NumberFormatException nfe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Invalid refresh frequency: " + freq); - return false; - } - } - - /** - * Call this when changing news URLs to force an update next time the timer fires. - * @since 0.8.7 - */ - void invalidateNews() { - _lastModified = null; - _invalidated = true; - } - - public void fetchNews() { - String newsURL = ConfigUpdateHelper.getNewsURL(_context); - boolean shouldProxy = Boolean.parseBoolean(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)); - String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); - int proxyPort = ConfigUpdateHandler.proxyPort(_context); - if (_tempFile.exists()) - _tempFile.delete(); - - try { - EepGet get = null; - if (shouldProxy) - get = new EepGet(_context, true, proxyHost, proxyPort, 0, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified); - else - get = new EepGet(_context, false, null, 0, 0, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified); - get.addStatusListener(this); - if (get.fetch()) { - _lastModified = get.getLastModified(); - _invalidated = false; - } else { - // backup news location - always proxied - _tempFile.delete(); - get = new EepGet(_context, true, proxyHost, proxyPort, 0, _tempFile.getAbsolutePath(), BACKUP_NEWS_URL, true, null, _lastModified); - get.addStatusListener(this); - if (get.fetch()) - _lastModified = get.getLastModified(); - } - } catch (Throwable t) { - _log.error("Error fetching the news", t); - } - } - - public boolean shouldFetchUnsigned() { - String url = _context.getProperty(ConfigUpdateHandler.PROP_ZIP_URL); - return url != null && url.length() > 0 && - _context.getBooleanProperty(ConfigUpdateHandler.PROP_UPDATE_UNSIGNED) && - !dontInstall(); - } - - /** - * HEAD the update url, and if the last-mod time is newer than the last update we - * downloaded, as stored in the properties, then we download it using eepget. - */ - public void fetchUnsignedHead() { - String url = _context.getProperty(ConfigUpdateHandler.PROP_ZIP_URL); - if (url == null || url.length() <= 0) - return; - // assume always proxied for now - //boolean shouldProxy = Boolean.parseBoolean(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)); - String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); - int proxyPort = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT); - - try { - EepHead get = new EepHead(_context, proxyHost, proxyPort, 0, url); - if (get.fetch()) { - String lastmod = get.getLastModified(); - if (lastmod != null) { - long modtime = RFC822Date.parse822Date(lastmod); - if (modtime <= 0) return; - String lastUpdate = _context.getProperty(UpdateHandler.PROP_LAST_UPDATE_TIME); - if (lastUpdate == null) { - // we don't know what version you have, so stamp it with the current time, - // and we'll look for something newer next time around. - _context.router().saveConfig(UpdateHandler.PROP_LAST_UPDATE_TIME, - Long.toString(_context.clock().now())); - return; - } - long ms = 0; - try { - ms = Long.parseLong(lastUpdate); - } catch (NumberFormatException nfe) {} - if (ms <= 0) return; - if (modtime > ms) { - _unsignedUpdateAvailable = true; - // '07-Jul 21:09 UTC' with month name in the system locale - _unsignedUpdateVersion = (new SimpleDateFormat("dd-MMM HH:mm")).format(new Date(modtime)) + " UTC"; - if (shouldInstall()) - fetchUnsigned(); - } - } - } - } catch (Throwable t) { - _log.error("Error fetching the unsigned update", t); - } - } - - public void fetchUnsigned() { - String url = _context.getProperty(ConfigUpdateHandler.PROP_ZIP_URL); - if (url == null || url.length() <= 0) - return; - UpdateHandler handler = new UnsignedUpdateHandler(_context, url, - _unsignedUpdateVersion); - handler.update(); - } - - private static final String VERSION_STRING = "version=\"" + RouterVersion.VERSION + "\""; - private static final String VERSION_PREFIX = "version=\""; - private void checkForUpdates() { - _updateAvailable = false; - if ( (!_newsFile.exists()) || (_newsFile.length() <= 0) ) return; - FileInputStream in = null; - try { - in = new FileInputStream(_newsFile); - StringBuilder buf = new StringBuilder(128); - while (DataHelper.readLine(in, buf)) { - int index = buf.indexOf(VERSION_PREFIX); - if (index == -1) { - // skip - } else { - int end = buf.indexOf("\"", index + VERSION_PREFIX.length()); - if (end > index) { - String ver = buf.substring(index+VERSION_PREFIX.length(), end); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Found version: [" + ver + "]"); - if (TrustedUpdate.needsUpdate(RouterVersion.VERSION, ver)) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Our version is out of date, update!"); - _updateVersion = ver; - break; - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Our version is current"); - return; - } - } - } - if (buf.indexOf(VERSION_STRING) != -1) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Our version found, no need to update: " + buf.toString()); - return; - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("No match in " + buf.toString()); - } - buf.setLength(0); - } - } catch (IOException ioe) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Error checking the news for an update", ioe); - return; - } finally { - if (in != null) try { in.close(); } catch (IOException ioe) {} - } - // could not find version="0.5.0.1", so there must be an update ;) - - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Our version was NOT found (" + RouterVersion.VERSION + "), update needed"); - _updateAvailable = !dontInstall(); - - if (shouldInstall()) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Policy requests update, so we update"); - UpdateHandler handler = new UpdateHandler(_context); - handler.update(); - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Policy requests manual update, so we do nothing"); - } - } - - public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) { - // ignore - } - public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) { - // ignore - } - public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { - if (_log.shouldLog(Log.INFO)) - _log.info("News fetched from " + url + " with " + (alreadyTransferred+bytesTransferred)); - - long now = _context.clock().now(); - if (_tempFile.exists()) { - boolean copied = FileUtil.copy(_tempFile, _newsFile, true, false); - if (copied) { - _lastUpdated = now; - _tempFile.delete(); - checkForUpdates(); - } else { - if (_log.shouldLog(Log.ERROR)) - _log.error("Failed to copy the news file!"); - } - } else { - if (_log.shouldLog(Log.WARN)) - _log.warn("Transfer complete, but no file? - probably 304 Not Modified"); - } - _lastFetch = now; - _context.router().saveConfig(PROP_LAST_CHECKED, Long.toString(now)); - } - - public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Failed to fetch the news from " + url); - _tempFile.delete(); - } - public void headerReceived(String url, int attemptNum, String key, String val) {} - public void attempting(String url) {} -} diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java index d5d702e8ff..f8a45b215c 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java @@ -2,6 +2,11 @@ package net.i2p.router.web; import java.io.File; +import net.i2p.data.DataHelper; +import net.i2p.router.RouterContext; +import net.i2p.router.update.ConsoleUpdateManager; +import static net.i2p.update.UpdateType.*; + /** * If news file does not exist, use file from the initialNews directory * in $I2P @@ -10,6 +15,86 @@ import java.io.File; */ public class NewsHelper extends ContentHelper { + public static final String PROP_LAST_UPDATE_TIME = "router.updateLastDownloaded"; + /** @since 0.8.12 */ + private static final String PROP_LAST_HIDDEN = "routerconsole.newsLastHidden"; + /** @since 0.9.2 */ + public static final String PROP_LAST_CHECKED = "routerconsole.newsLastChecked"; + /** @since 0.9.2 */ + public static final String PROP_LAST_UPDATED = "routerconsole.newsLastUpdated"; + public static final String NEWS_FILE = "docs/news.xml"; + + /** + * If ANY update is in progress. + * @since 0.9.2 was stored in system properties + */ + public static boolean isAnyUpdateInProgress() { + ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); + if (mgr == null) return false; + return mgr.isUpdateInProgress(); + } + + /** + * If a signed or unsigned router update is in progress. + * Does NOT cover plugins, news, etc. + * @since 0.9.2 was stored in system properties + */ + public static boolean isUpdateInProgress() { + ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); + if (mgr == null) return false; + return mgr.isUpdateInProgress(ROUTER_SIGNED) || + mgr.isUpdateInProgress(ROUTER_UNSIGNED) || + mgr.isUpdateInProgress(TYPE_DUMMY); + } + + /** + * @since 0.9.2 moved from NewsFetcher + */ + public static boolean isUpdateAvailable() { + ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); + if (mgr == null) return false; + return mgr.getUpdateAvailable(ROUTER_SIGNED) != null; + } + + /** + * @return null if none + * @since 0.9.2 moved from NewsFetcher + */ + public static String updateVersion() { + ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); + if (mgr == null) return null; + return mgr.getUpdateAvailable(ROUTER_SIGNED); + } + + /** + * @since 0.9.2 moved from NewsFetcher + */ + public static boolean isUnsignedUpdateAvailable() { + ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); + if (mgr == null) return false; + return mgr.getUpdateAvailable(ROUTER_UNSIGNED) != null; + } + + /** + * @return null if none + * @since 0.9.2 moved from NewsFetcher + */ + public static String unsignedUpdateVersion() { + ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); + if (mgr == null) return null; + return mgr.getUpdateAvailable(ROUTER_UNSIGNED); + } + + /** + * @return "" if none + * @since 0.9.2 moved from UpdateHelper + */ + public static String getUpdateStatus() { + ConsoleUpdateManager mgr = ConsoleUpdateManager.getInstance(); + if (mgr == null) return ""; + return mgr.getStatus(); + } + @Override public String getContent() { File news = new File(_page); @@ -18,8 +103,131 @@ public class NewsHelper extends ContentHelper { return super.getContent(); } - /** @since 0.8.12 */ + /** + * Is the news newer than the last time it was hidden? + * @since 0.8.12 + */ public boolean shouldShowNews() { - return NewsFetcher.getInstance(_context).shouldShowNews(); + return shouldShowNews(_context); + } + + /** + * @since 0.9.2 + */ + public static boolean shouldShowNews(RouterContext ctx) { + long lastUpdated = lastUpdated(ctx); + if (lastUpdated <= 0) + return true; + String h = ctx.getProperty(PROP_LAST_HIDDEN); + if (h == null) + return true; + long last = 0; + try { + last = Long.parseLong(h); + } catch (NumberFormatException nfe) {} + return lastUpdated > last; + } + + /** + * Save config with the timestamp of the current news to hide, or 0 to show + * @since 0.8.12 + */ + public void showNews(boolean yes) { + showNews(_context, yes); + } + + /** + * Save config with the timestamp of the current news to hide, or 0 to show + * @since 0.9.2 + */ + public static void showNews(RouterContext ctx, boolean yes) { + long lastUpdated = 0; +/////// FIME from props, or from last mod time? + long stamp = yes ? 0 : lastUpdated; + ctx.router().saveConfig(PROP_LAST_HIDDEN, Long.toString(stamp)); + } + + /** + * @return HTML + * @since 0.9.2 moved from NewsFetcher + */ + public String status() { + return status(_context); + } + + /** + * @return HTML + * @since 0.9.2 moved from NewsFetcher + */ + public static String status(RouterContext ctx) { + StringBuilder buf = new StringBuilder(128); + long now = ctx.clock().now(); + buf.append(""); + long lastUpdated = lastUpdated(ctx); + long lastFetch = lastChecked(ctx); + if (lastUpdated > 0) { + buf.append(Messages.getString("News last updated {0} ago.", + DataHelper.formatDuration2(now - lastUpdated), + ctx)) + .append('\n'); + } + if (lastFetch > lastUpdated) { + buf.append(Messages.getString("News last checked {0} ago.", + DataHelper.formatDuration2(now - lastFetch), + ctx)); + } + buf.append(""); + String consoleNonce = System.getProperty("router.consoleNonce"); + if (lastUpdated > 0 && consoleNonce != null) { + if (shouldShowNews(ctx)) { + buf.append(" ") + .append(Messages.getString("Hide news", ctx)); + } else { + buf.append(" ") + .append(Messages.getString("Show news", ctx)); + } + buf.append(""); + } + return buf.toString(); + } + + /** + * @since 0.9.2 moved from NewsFetcher + */ + public static boolean dontInstall(RouterContext ctx) { + File test = new File(ctx.getBaseDir(), "history.txt"); + boolean readonly = ((test.exists() && !test.canWrite()) || (!ctx.getBaseDir().canWrite())); + boolean disabled = ctx.getBooleanProperty(ConfigUpdateHandler.PROP_UPDATE_DISABLED); + return readonly || disabled; + } + + /** + * @since 0.9.2 + */ + public static long lastChecked(RouterContext ctx) { + String lc = ctx.getProperty(PROP_LAST_CHECKED); + if (lc == null) { + try { + return Long.parseLong(lc); + } catch (NumberFormatException nfe) {} + } + return 0; + } + + /** + * When the news was last downloaded + * @since 0.9.2 + */ + public static long lastUpdated(RouterContext ctx) { + String lc = ctx.getProperty(PROP_LAST_UPDATED); + if (lc == null) { + try { + return Long.parseLong(lc); + } catch (NumberFormatException nfe) {} + } + File newsFile = new File(ctx.getRouterDir(), NEWS_FILE); + long rv = newsFile.lastModified(); + ctx.router().saveConfig(PROP_LAST_UPDATED, Long.toString(rv)); + return rv; } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java index 60e9dd8c3a..6d70a9dbf0 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -25,6 +25,8 @@ import net.i2p.router.RouterContext; import net.i2p.router.RouterVersion; import net.i2p.router.startup.ClientAppConfig; import net.i2p.router.startup.LoadClientAppsJob; +import net.i2p.router.update.ConsoleUpdateManager; +import static net.i2p.update.UpdateType.*; import net.i2p.util.ConcurrentHashSet; import net.i2p.util.FileUtil; import net.i2p.util.I2PAppThread; @@ -45,8 +47,9 @@ import org.mortbay.jetty.handler.ContextHandlerCollection; */ public class PluginStarter implements Runnable { protected RouterContext _context; - static final String PREFIX = "plugin."; - static final String ENABLED = ".startOnLoad"; + public static final String PREFIX = "plugin."; + public static final String ENABLED = ".startOnLoad"; + public static final String PLUGIN_DIR = "plugins"; private static final String[] STANDARD_WEBAPPS = { "i2psnark", "i2ptunnel", "susidns", "susimail", "addressbook", "routerconsole" }; private static final String[] STANDARD_THEMES = { "images", "light", "dark", "classic", @@ -66,7 +69,7 @@ public class PluginStarter implements Runnable { public void run() { if (_context.getBooleanPropertyDefaultTrue("plugins.autoUpdate") && - (!Boolean.parseBoolean(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) && + (!NewsHelper.isUpdateInProgress()) && (!RouterVersion.VERSION.equals(_context.getProperty("router.previousVersion")))) updateAll(_context, true); startPlugins(_context); @@ -112,18 +115,24 @@ public class PluginStarter implements Runnable { } if (toUpdate.isEmpty()) return; - PluginUpdateChecker puc = PluginUpdateChecker.getInstance(ctx); - if (puc.isRunning()) + + ConsoleUpdateManager mgr = (ConsoleUpdateManager) ctx.updateManager(); + if (mgr == null) + return; + if (mgr.isUpdateInProgress()) return; if (delay) { // wait for proxy - System.setProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS, "true"); - puc.setAppStatus(Messages.getString("Checking for plugin updates", ctx)); - try { - Thread.sleep(3*60*1000); - } catch (InterruptedException ie) {} - System.setProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS, "false"); + mgr.update(TYPE_DUMMY, 3*60*1000); + mgr.notifyProgress(null, Messages.getString("Checking for plugin updates", ctx)); + int loop = 0; + do { + try { + Thread.sleep(5*1000); + } catch (InterruptedException ie) {} + if (loop++ > 40) break; + } while (mgr.isUpdateInProgress(TYPE_DUMMY)); } Log log = ctx.logManager().getLog(PluginStarter.class); @@ -132,34 +141,32 @@ public class PluginStarter implements Runnable { String appName = entry.getKey(); if (log.shouldLog(Log.WARN)) log.warn("Checking for update plugin: " + appName); - puc.update(appName); - do { - try { - Thread.sleep(5*1000); - } catch (InterruptedException ie) {} - } while (puc.isRunning()); - if (!puc.isNewerAvailable()) { + + // blocking + if (mgr.checkAvailable(PLUGIN, appName, 60*1000) == null) { if (log.shouldLog(Log.WARN)) log.warn("No update available for plugin: " + appName); continue; } - PluginUpdateHandler puh = PluginUpdateHandler.getInstance(ctx); - String url = entry.getValue(); + if (log.shouldLog(Log.WARN)) log.warn("Updating plugin: " + appName); - puh.update(url); + mgr.update(PLUGIN, appName, 30*60*1000); + int loop = 0; do { try { Thread.sleep(5*1000); } catch (InterruptedException ie) {} - } while (puh.isRunning()); - if (puh.wasUpdateSuccessful()) + if (loop++ > 40) break; + } while (mgr.isUpdateInProgress(PLUGIN, appName)); + + if (mgr.getUpdateAvailable(PLUGIN, appName) != null) updated++; } if (updated > 0) - puc.setDoneStatus(ngettext("1 plugin updated", "{0} plugins updated", updated, ctx)); + mgr.notifyComplete(null, ngettext("1 plugin updated", "{0} plugins updated", updated, ctx)); else - puc.setDoneStatus(Messages.getString("Plugin update check complete", ctx)); + mgr.notifyComplete(null, Messages.getString("Plugin update check complete", ctx)); } /** this shouldn't throw anything */ @@ -189,9 +196,9 @@ public class PluginStarter implements Runnable { * @return true on success * @throws just about anything, caller would be wise to catch Throwable */ - static boolean startPlugin(RouterContext ctx, String appName) throws Exception { + public static boolean startPlugin(RouterContext ctx, String appName) throws Exception { Log log = ctx.logManager().getLog(PluginStarter.class); - File pluginDir = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName); + File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName); if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) { log.error("Cannot start nonexistent plugin: " + appName); disablePlugin(appName); @@ -199,7 +206,7 @@ public class PluginStarter implements Runnable { } // Do we need to extract an update? - File pluginUpdate = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName + "/app.xpi2p.zip" ); + File pluginUpdate = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName + "/app.xpi2p.zip" ); if(pluginUpdate.exists()) { // Compare the start time of the router with the plugin. if(ctx.router().getWhenStarted() > pluginUpdate.lastModified()) { @@ -363,9 +370,9 @@ public class PluginStarter implements Runnable { * @return true on success * @throws just about anything, caller would be wise to catch Throwable */ - static boolean stopPlugin(RouterContext ctx, String appName) throws Exception { + public static boolean stopPlugin(RouterContext ctx, String appName) throws Exception { Log log = ctx.logManager().getLog(PluginStarter.class); - File pluginDir = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName); + File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName); if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) { log.error("Cannot stop nonexistent plugin: " + appName); return false; @@ -424,7 +431,7 @@ public class PluginStarter implements Runnable { /** @return true on success - caller should call stopPlugin() first */ static boolean deletePlugin(RouterContext ctx, String appName) throws Exception { Log log = ctx.logManager().getLog(PluginStarter.class); - File pluginDir = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName); + File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName); if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) { log.error("Cannot delete nonexistent plugin: " + appName); return false; @@ -469,7 +476,7 @@ public class PluginStarter implements Runnable { /** plugin.config */ public static Properties pluginProperties(I2PAppContext ctx, String appName) { - File cfgFile = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName + '/' + "plugin.config"); + File cfgFile = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName + '/' + "plugin.config"); Properties rv = new Properties(); try { DataHelper.loadProps(rv, cfgFile); @@ -530,7 +537,7 @@ public class PluginStarter implements Runnable { */ public static ListHandles the request to update the router by firing off an - * {@link net.i2p.util.EepGet} call to download the latest unsigned zip file - * and displaying the status to anyone who asks. - *
- *After the download completes the signed update file is copied to the - * router directory, and if configured the router is restarted to complete - * the update process. - *
- */ -public class UnsignedUpdateHandler extends UpdateHandler { - private static UnsignedUpdateRunner _unsignedUpdateRunner; - private String _zipURL; - private String _zipVersion; - - public UnsignedUpdateHandler(RouterContext ctx, String zipURL, String version) { - super(ctx); - _zipURL = zipURL; - _zipVersion = version; - _updateFile = (new File(ctx.getTempDir(), "tmp" + ctx.random().nextInt() + Router.UPDATE_FILE)).getAbsolutePath(); - } - - @Override - public void update() { - // don't block waiting for the other one to finish - if ("true".equals(System.getProperty(PROP_UPDATE_IN_PROGRESS))) { - _log.error("Update already running"); - return; - } - synchronized (UpdateHandler.class) { - if (_unsignedUpdateRunner == null) { - _unsignedUpdateRunner = new UnsignedUpdateRunner(); - } - if (_unsignedUpdateRunner.isRunning()) { - return; - } else { - System.setProperty(PROP_UPDATE_IN_PROGRESS, "true"); - I2PAppThread update = new I2PAppThread(_unsignedUpdateRunner, "UnsignedUpdate"); - update.start(); - } - } - } - - /** - * Eepget the .zip file to the temp dir, then copy it over - */ - public class UnsignedUpdateRunner extends UpdateRunner implements Runnable, EepGet.StatusListener { - public UnsignedUpdateRunner() { - super(); - } - - /** Get the file */ - @Override - protected void update() { - updateStatus("" + _("Updating") + ""); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Starting unsigned update URL: " + _zipURL); - // always proxy for now - //boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue(); - String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); - int proxyPort = ConfigUpdateHandler.proxyPort(_context); - try { - // 40 retries!! - _get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, _zipURL, false); - _get.addStatusListener(UnsignedUpdateRunner.this); - _get.fetch(CONNECT_TIMEOUT, -1, INACTIVITY_TIMEOUT); - } catch (Throwable t) { - _log.error("Error updating", t); - } - } - - /** eepget listener callback Overrides */ - @Override - public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { - File updFile = new File(_updateFile); - if (FileUtil.verifyZip(updFile)) { - updateStatus("" + _("Update downloaded") + ""); - } else { - updFile.delete(); - updateStatus("" + _("Unsigned update file from {0} is corrupt", url) + ""); - _log.log(Log.CRIT, "Corrupt zip file from " + url); - return; - } - File to = new File(_context.getRouterDir(), Router.UPDATE_FILE); - boolean copied = FileUtil.copy(updFile, to, true, false); - if (copied) { - updFile.delete(); - String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY); - this.done = true; - String lastmod = _get.getLastModified(); - long modtime = 0; - if (lastmod != null) - modtime = RFC822Date.parse822Date(lastmod); - if (modtime <= 0) - modtime = _context.clock().now(); - _context.router().saveConfig(PROP_LAST_UPDATE_TIME, "" + modtime); - if ("install".equals(policy)) { - _log.log(Log.CRIT, "Update was downloaded, restarting to install it"); - updateStatus("" + _("Update downloaded") + "Handles the request to update the router by firing one or more @@ -31,31 +16,22 @@ import net.i2p.util.VersionComparator; * of the signed update file is unpacked and the router is restarted to complete * the update process. *
+ * + * This is like a FormHandler but we don't extend it, as we don't have the message area, etc. */ public class UpdateHandler { - protected static UpdateRunner _updateRunner; protected RouterContext _context; protected Log _log; - protected String _updateFile; - private static String _status = ""; private String _action; private String _nonce; - protected static final String SIGNED_UPDATE_FILE = "i2pupdate.sud"; - static final String PROP_UPDATE_IN_PROGRESS = "net.i2p.router.web.UpdateHandler.updateInProgress"; - protected static final String PROP_LAST_UPDATE_TIME = "router.updateLastDownloaded"; - - protected static final long CONNECT_TIMEOUT = 55*1000; - protected static final long INACTIVITY_TIMEOUT = 5*60*1000; - protected static final long NOPROXY_INACTIVITY_TIMEOUT = 60*1000; - public UpdateHandler() { this(ContextHelper.getContext(null)); } + public UpdateHandler(RouterContext ctx) { _context = ctx; _log = ctx.logManager().getLog(UpdateHandler.class); - _updateFile = (new File(ctx.getRouterDir(), SIGNED_UPDATE_FILE)).getAbsolutePath(); } /** @@ -89,274 +65,21 @@ public class UpdateHandler { if (_nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.nonce")) || _nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.noncePrev"))) { if (_action.contains("Unsigned")) { - // Not us, have NewsFetcher instantiate the correct class. - NewsFetcher fetcher = NewsFetcher.getInstance(_context); - fetcher.fetchUnsigned(); + update(ROUTER_UNSIGNED); } else { - update(); + update(ROUTER_SIGNED); } } } - public void update() { - // don't block waiting for the other one to finish - if ("true".equals(System.getProperty(PROP_UPDATE_IN_PROGRESS))) { + private void update(UpdateType type) { + ConsoleUpdateManager mgr = (ConsoleUpdateManager) _context.updateManager(); + if (mgr == null) + return; + if (mgr.isUpdateInProgress(ROUTER_SIGNED) || mgr.isUpdateInProgress(ROUTER_UNSIGNED)) { _log.error("Update already running"); return; } - synchronized (UpdateHandler.class) { - if (_updateRunner == null) - _updateRunner = new UpdateRunner(); - if (_updateRunner.isRunning()) { - return; - } else { - System.setProperty(PROP_UPDATE_IN_PROGRESS, "true"); - I2PAppThread update = new I2PAppThread(_updateRunner, "SignedUpdate"); - update.start(); - } - } + mgr.update(type); } - - public static String getStatus() { - return _status; - } - - public boolean isDone() { - return false; - // this needs to be fixed and tested - //if(this._updateRunner == null) - // return true; - //return this._updateRunner.isDone(); - } - - public class UpdateRunner implements Runnable, EepGet.StatusListener { - protected volatile boolean _isRunning; - protected boolean done; - protected EepGet _get; - protected final DecimalFormat _pct = new DecimalFormat("0.0%"); - /** tells the listeners what mode we are in */ - private boolean _isPartial; - /** set by the listeners on completion */ - private boolean _isNewer; - private ByteArrayOutputStream _baos; - - public UpdateRunner() { - _isRunning = false; - this.done = false; - updateStatus("" + _("Updating") + ""); - } - public boolean isRunning() { return _isRunning; } - public boolean isDone() { - return this.done; - } - public void run() { - _isRunning = true; - update(); - System.setProperty(PROP_UPDATE_IN_PROGRESS, "false"); - _isRunning = false; - } - - /** - * Loop through the entire list of update URLs. - * For each one, first get the version from the first 56 bytes and see if - * it is newer than what we are running now. - * If it is, get the whole thing. - */ - protected void update() { - // Do a PartialEepGet on the selected URL, check for version we expect, - // and loop if it isn't what we want. - // This will allows us to do a release without waiting for the last host to install the update. - // Alternative: In bytesTransferred(), Check the data in the output file after - // we've received at least 56 bytes. Need a cancel() method in EepGet ? - - boolean shouldProxy = Boolean.parseBoolean(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)); - String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); - int proxyPort = ConfigUpdateHandler.proxyPort(_context); - - List+Interfaces for classes to assist in the update process without +needing the router context. +
+ + diff --git a/core/java/src/net/i2p/util/VersionComparator.java b/core/java/src/net/i2p/util/VersionComparator.java index 3b6e97f5b4..8c72252fcb 100644 --- a/core/java/src/net/i2p/util/VersionComparator.java +++ b/core/java/src/net/i2p/util/VersionComparator.java @@ -22,7 +22,7 @@ public class VersionComparator implements Comparator