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 256babbe3c6dbe6a1ea83ba4e8db8127a64c1914..d0a3fa92c361f657c073796dacf5c5e62cd007e2 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java @@ -54,7 +54,7 @@ public class ConfigUpdateHandler extends FormHandler { NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext()); fetcher.fetchNews(); if (fetcher.shouldFetchUnsigned()) - fetcher.fetchUnsigned(); + fetcher.fetchUnsignedHead(); if (fetcher.updateAvailable() || fetcher.unsignedUpdateAvailable()) { if ( (_updatePolicy == null) || (!_updatePolicy.equals("notify")) ) addFormNotice("Update available, attempting to download now"); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java index 447273a8870875218079a7e4a5b31e77ad6ed010..11848ef46a2e8e691da0cc86781e87390fca3eab 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java @@ -188,8 +188,8 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { if (ms <= 0) return; if (modtime > ms) { _unsignedUpdateAvailable = true; - // '07-Jul 21:09' with month name in the system locale - _unsignedUpdateVersion = (new SimpleDateFormat("dd-MMM HH:mm")).format(new Date(modtime)); + // '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(); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UnsignedUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UnsignedUpdateHandler.java index 0d812d0ec0e225f3750af41d1c69f1a075fed9c0..a89a7a05c7e7f7e60b1e1ec2532e9c2eb75a0ce9 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/UnsignedUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UnsignedUpdateHandler.java @@ -21,6 +21,7 @@ import net.i2p.util.Log; * </p> */ public class UnsignedUpdateHandler extends UpdateHandler { + private static UnsignedUpdateRunner _unsignedUpdateRunner; private String _zipURL; private String _zipVersion; @@ -34,19 +35,19 @@ public class UnsignedUpdateHandler extends UpdateHandler { @Override public void update() { // don't block waiting for the other one to finish - if ("true".equals(System.getProperty(PROP_UPDATE_IN_PROGRESS, "false"))) { + if ("true".equals(System.getProperty(PROP_UPDATE_IN_PROGRESS))) { _log.error("Update already running"); return; } synchronized (UpdateHandler.class) { - if (_updateRunner == null) { - _updateRunner = new UnsignedUpdateRunner(); + if (_unsignedUpdateRunner == null) { + _unsignedUpdateRunner = new UnsignedUpdateRunner(); } - if (_updateRunner.isRunning()) { + if (_unsignedUpdateRunner.isRunning()) { return; } else { System.setProperty(PROP_UPDATE_IN_PROGRESS, "true"); - I2PAppThread update = new I2PAppThread(_updateRunner, "Update"); + I2PAppThread update = new I2PAppThread(_unsignedUpdateRunner, "UnsignedUpdate"); update.start(); } } @@ -64,6 +65,8 @@ public class UnsignedUpdateHandler extends UpdateHandler { @Override protected void update() { _status = "<b>Updating</b>"; + 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); @@ -74,19 +77,26 @@ public class UnsignedUpdateHandler extends UpdateHandler { _get.addStatusListener(UnsignedUpdateRunner.this); _get.fetch(); } catch (Throwable t) { - _context.logManager().getLog(UpdateHandler.class).error("Error updating", 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) { - _status = "<b>Update downloaded</b>"; - // we should really verify zipfile integrity here but there's no easy way to do it - String to = (new File(_context.getBaseDir(), Router.UPDATE_FILE)).getAbsolutePath(); + File updFile = new File(_updateFile); + if (FileUtil.verifyZip(updFile)) { + _status = "<b>Update downloaded</b>"; + } else { + updFile.delete(); + _status = "<b>Unsigned update file is corrupt</b> from " + url; + _log.log(Log.CRIT, "Corrupt zip file from " + url); + return; + } + String to = (new File(_context.getRouterDir(), Router.UPDATE_FILE)).getAbsolutePath(); boolean copied = FileUtil.copy(_updateFile, to, true); if (copied) { - (new File(_updateFile)).delete(); + updFile.delete(); String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY); this.done = true; String lastmod = _get.getLastModified(); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java index f8890482db0b19a8ba1962af8d4da2c70bcd24b9..293743bbbc6c71b0713dd619be8ad2d576021676 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java @@ -8,11 +8,12 @@ import java.util.StringTokenizer; import net.i2p.I2PAppContext; 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.util.EepGet; -import net.i2p.util.I2PThread; +import net.i2p.util.I2PAppThread; import net.i2p.util.Log; /** @@ -31,7 +32,9 @@ public class UpdateHandler { protected RouterContext _context; protected Log _log; protected String _updateFile; + protected static String _status = ""; private String _action; + private String _nonce; protected static final String SIGNED_UPDATE_FILE = "i2pupdate.sud"; protected static final String PROP_UPDATE_IN_PROGRESS = "net.i2p.router.web.UpdateHandler.updateInProgress"; @@ -61,13 +64,22 @@ public class UpdateHandler { } } - public void setUpdateAction(String val) { _action = val; } + /** these two can be set in either order, so call checkUpdateAction() twice */ + public void setUpdateAction(String val) { + _action = val; + checkUpdateAction(); + } public void setUpdateNonce(String nonce) { - if (nonce == null) return; - if (nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.nonce")) || - nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.noncePrev"))) { - if (_action != null && _action.contains("Unsigned")) { + _nonce = nonce; + checkUpdateAction(); + } + + private void checkUpdateAction() { + if (_nonce == null || _action == null) return; + 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(); @@ -90,16 +102,14 @@ public class UpdateHandler { return; } else { System.setProperty(PROP_UPDATE_IN_PROGRESS, "true"); - I2PThread update = new I2PThread(_updateRunner, "Update"); + I2PAppThread update = new I2PAppThread(_updateRunner, "SignedUpdate"); update.start(); } } } - public String getStatus() { - if (_updateRunner == null) - return ""; - return _updateRunner.getStatus(); + public static String getStatus() { + return _status; } public boolean isDone() { @@ -113,7 +123,6 @@ public class UpdateHandler { public class UpdateRunner implements Runnable, EepGet.StatusListener { protected boolean _isRunning; protected boolean done; - protected String _status; protected EepGet _get; private final DecimalFormat _pct = new DecimalFormat("0.0%"); @@ -126,7 +135,6 @@ public class UpdateHandler { public boolean isDone() { return this.done; } - public String getStatus() { return _status; } public void run() { _isRunning = true; update(); @@ -150,7 +158,7 @@ public class UpdateHandler { _get.addStatusListener(UpdateRunner.this); _get.fetch(); } catch (Throwable t) { - _context.logManager().getLog(UpdateHandler.class).error("Error updating", t); + _log.error("Error updating", t); } } @@ -167,15 +175,16 @@ public class UpdateHandler { synchronized (_pct) { buf.append(_pct.format(pct)); } - buf.append(":<br />\n" + (currentWrite + alreadyTransferred)); - buf.append(" transferred"); + buf.append(":<br>\n"); + buf.append(DataHelper.formatSize(currentWrite + alreadyTransferred)); + buf.append("B transferred"); _status = buf.toString(); } public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { _status = "<b>Update downloaded</b>"; TrustedUpdate up = new TrustedUpdate(_context); File f = new File(_updateFile); - File to = new File(_context.getBaseDir(), Router.UPDATE_FILE); + File to = new File(_context.getRouterDir(), Router.UPDATE_FILE); String err = up.migrateVerified(RouterVersion.VERSION, f, to); f.delete(); if (err == null) { @@ -233,9 +242,9 @@ public class UpdateHandler { while (tok.hasMoreTokens()) URLList.add(tok.nextToken().trim()); int size = URLList.size(); - _log.log(Log.DEBUG, "Picking update source from " + size + " candidates."); + //_log.log(Log.DEBUG, "Picking update source from " + size + " candidates."); if (size <= 0) { - _log.log(Log.WARN, "Update list is empty - no update available"); + _log.log(Log.CRIT, "Update source list is empty - cannot download update"); return null; } int index = I2PAppContext.getGlobalContext().random().nextInt(size); diff --git a/apps/routerconsole/jsp/summarynoframe.jsp b/apps/routerconsole/jsp/summarynoframe.jsp index eae7de252db6ee698facbfd25e7e740f7e85ad93..507934049b00bb1b00982c453ca8f0de18860599 100644 --- a/apps/routerconsole/jsp/summarynoframe.jsp +++ b/apps/routerconsole/jsp/summarynoframe.jsp @@ -60,7 +60,7 @@ <% if (helper.updateAvailable() || helper.unsignedUpdateAvailable()) { // display all the time so we display the final failure message - out.print("<br />" + update.getStatus()); + out.print("<br>" + net.i2p.router.web.UpdateHandler.getStatus()); if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress"))) { } else if((!update.isDone()) && request.getParameter("action") == null && @@ -76,7 +76,7 @@ if (helper.updateAvailable()) out.print("<button type=\"submit\" name=\"updateAction\" value=\"signed\" >Download " + helper.getUpdateVersion() + " Update</button>\n"); if (helper.unsignedUpdateAvailable()) - out.print("<button type=\"submit\" name=\"updateAction\" value=\"Unsigned\" >Download Unsigned<br />" + helper.getUnsignedUpdateVersion() + " Update</button>\n"); + out.print("<button type=\"submit\" name=\"updateAction\" value=\"Unsigned\" >Download Unsigned<br>Update " + helper.getUnsignedUpdateVersion() + "</button>\n"); out.print("</form></p>\n"); } } diff --git a/core/java/src/net/i2p/util/FileUtil.java b/core/java/src/net/i2p/util/FileUtil.java index 38073fd6054a5a20de2ded809ff8cefa27ab9b4d..ab0341f411de0d9219bf057bf704927b82b7ce50 100644 --- a/core/java/src/net/i2p/util/FileUtil.java +++ b/core/java/src/net/i2p/util/FileUtil.java @@ -137,6 +137,56 @@ public class FileUtil { } } + /** + * Verify the integrity of a zipfile. + * There doesn't seem to be any library function to do this, + * so we basically go through all the motions of extractZip() above, + * unzipping everything but throwing away the data. + * + * @return true if ok + */ + public static boolean verifyZip(File zipfile) { + ZipFile zip = null; + try { + byte buf[] = new byte[16*1024]; + zip = new ZipFile(zipfile); + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = (ZipEntry)entries.nextElement(); + if (entry.getName().indexOf("..") != -1) { + //System.err.println("ERROR: Refusing to extract a zip entry with '..' in it [" + entry.getName() + "]"); + return false; + } + if (entry.isDirectory()) { + // noop + } else { + try { + InputStream in = zip.getInputStream(entry); + int read = 0; + while ( (read = in.read(buf)) != -1) { + // throw the data away + } + //System.err.println("INFO: File [" + entry.getName() + "] extracted"); + in.close(); + } catch (IOException ioe) { + //System.err.println("ERROR: Error extracting the zip entry (" + entry.getName() + "]"); + //ioe.printStackTrace(); + return false; + } + } + } + return true; + } catch (IOException ioe) { + //System.err.println("ERROR: Unable to extract the zip file"); + //ioe.printStackTrace(); + return false; + } finally { + if (zip != null) { + try { zip.close(); } catch (IOException ioe) {} + } + } + } + /** * Read in the last few lines of a (newline delimited) textfile, or null if * the file doesn't exist.