2005-03-23 jrandom

* New /configupdate.jsp page for controlling the update / notification
      process, as well as various minor related updates.  Note that not all
      options are exposed yet, and the update detection code isn't in place
      in this commit - it currently says there is always an update available.
    * New EepGet component for reliable downloading, with a CLI exposed in
      java -cp lib/i2p.jar net.i2p.util.EepGet url
    * Added a default signing key to the TrustedUpdate component to be used
      for verifying updates.  This signing key can be authenticated via
      gpg --verify i2p/core/java/src/net/i2p/crypto/TrustedUpdate.java
    * New public domain SHA1 implementation for the DSA code so that we can
      handle signing streams of arbitrary size without excess memory usage
      (thanks P.Verdy!)
    * Added some helpers to the TrustedUpdate to work off streams and to offer
      a minimal CLI:
          TrustedUpdate keygen pubKeyFile privKeyFile
          TrustedUpdate sign origFile signedFile privKeyFile
          TrustedUpdate verify signedFile
This commit is contained in:
jrandom
2005-03-23 21:13:03 +00:00
committed by zzz
parent 677eeac8f7
commit a2c309ddd3
18 changed files with 2149 additions and 201 deletions

View File

@ -20,7 +20,7 @@ import org.tanukisoftware.wrapper.WrapperManager;
*/
public class ConfigServiceHandler extends FormHandler {
private class UpdateWrapperManagerTask implements Runnable {
public static class UpdateWrapperManagerTask implements Runnable {
private int _exitCode;
public UpdateWrapperManagerTask(int exitCode) {
_exitCode = exitCode;

View File

@ -0,0 +1,83 @@
package net.i2p.router.web;
import net.i2p.data.DataHelper;
/**
*
*/
public class ConfigUpdateHandler extends FormHandler {
private String _newsURL;
private long _refreshFrequency;
private String _updateURL;
private String _updatePolicy;
private boolean _updateThroughProxy;
private String _trustedKeys;
public static final String PROP_NEWS_URL = "router.newsURL";
public static final String DEFAULT_NEWS_URL = "http://www.i2p/routerConsoleNews.xml";
public static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency";
public static final String DEFAULT_REFRESH_FREQUENCY = 24*60*60*1000 + "";
public static final String PROP_UPDATE_URL = "router.updateURL";
public static final String DEFAULT_UPDATE_URL = "http://dev.i2p.net/i2p/i2pupdate.sud";
public static final String PROP_UPDATE_POLICY = "router.updatePolicy";
public static final String DEFAULT_UPDATE_POLICY = "notify";
public static final String PROP_SHOULD_PROXY = "router.updateThroughProxy";
public static final String DEFAULT_SHOULD_PROXY = Boolean.FALSE.toString();
public static final String PROP_PROXY_HOST = "router.updateProxyHost";
public static final String DEFAULT_PROXY_HOST = "localhost";
public static final String PROP_PROXY_PORT = "router.updateProxyPort";
public static final String DEFAULT_PROXY_PORT = "4444";
protected void processForm() {
if ( (_newsURL != null) && (_newsURL.length() > 0) ) {
String oldURL = _context.router().getConfigSetting(PROP_NEWS_URL);
if ( (oldURL == null) || (!_newsURL.equals(oldURL)) ) {
_context.router().setConfigSetting(PROP_NEWS_URL, _newsURL);
addFormNotice("Updating news URL to " + _newsURL);
}
}
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
_context.router().setConfigSetting(PROP_UPDATE_URL, _updateURL);
addFormNotice("Updating update URL to " + _updateURL);
}
}
if (_updateThroughProxy) {
_context.router().setConfigSetting(PROP_SHOULD_PROXY, Boolean.TRUE.toString());
} else {
_context.router().setConfigSetting(PROP_SHOULD_PROXY, Boolean.FALSE.toString());
}
String oldFreqStr = _context.router().getConfigSetting(PROP_REFRESH_FREQUENCY);
long oldFreq = -1;
if (oldFreqStr != null)
try { oldFreq = Long.parseLong(oldFreqStr); } catch (NumberFormatException nfe) {}
if (_refreshFrequency != oldFreq) {
_context.router().setConfigSetting(PROP_REFRESH_FREQUENCY, ""+_refreshFrequency);
addFormNotice("Updating refresh frequency to " + DataHelper.formatDuration(_refreshFrequency));
}
if ( (_updatePolicy != null) && (_updatePolicy.length() > 0) ) {
String oldPolicy = _context.router().getConfigSetting(PROP_UPDATE_POLICY);
if ( (oldPolicy == null) || (!_updatePolicy.equals(oldPolicy)) ) {
_context.router().setConfigSetting(PROP_UPDATE_POLICY, _updatePolicy);
addFormNotice("Updating update policy to " + _updatePolicy);
}
}
// should save the keys...
_context.router().saveConfig();
}
public void setNewsURL(String url) { _newsURL = url; }
public void setRefreshFrequency(String freq) {
try { _refreshFrequency = Long.parseLong(freq); } catch (NumberFormatException nfe) {}
}
public void setUpdateURL(String url) { _updateURL = url; }
public void setUpdatePolicy(String policy) { _updatePolicy = policy; }
public void setTrustedKeys(String keys) { _trustedKeys = keys; }
public void setUpdateThroughProxy(String foo) { _updateThroughProxy = true; }
}

View File

@ -0,0 +1,76 @@
package net.i2p.router.web;
import java.util.List;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.router.RouterContext;
public class ConfigUpdateHelper {
private RouterContext _context;
/**
* Configure this bean to query a particular router context
*
* @param contextId begging few characters of the routerHash, or null to pick
* the first one we come across.
*/
public void setContextId(String contextId) {
try {
_context = ContextHelper.getContext(contextId);
} catch (Throwable t) {
t.printStackTrace();
}
}
public ConfigUpdateHelper() {}
public boolean updateAvailable() {
return true;
}
public String getNewsURL() {
String url = _context.getProperty(ConfigUpdateHandler.PROP_NEWS_URL);
if (url != null)
return url;
else
return ConfigUpdateHandler.DEFAULT_NEWS_URL;
}
public String getUpdateURL() {
String url = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL);
if (url != null)
return url;
else
return ConfigUpdateHandler.DEFAULT_UPDATE_URL;
}
public String getUpdateThroughProxy() {
String proxy = _context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY);
if (Boolean.valueOf(proxy).booleanValue())
return "<input type=\"checkbox\" value=\"true\" name=\"updateThroughProxy\" checked=\"true\" >";
else
return "<input type=\"checkbox\" value=\"true\" name=\"updateThroughProxy\" >";
}
public String getRefreshFrequencySelectBox() {
return "<select name=\"refreshFrequency\">" +
"<option value=\"" + (12*60*60*1000) + "\">Twice daily</option>" +
"<option value=\"" + (24*60*60*1000) + "\" selected=\"true\" >Daily</option>" +
"<option value=\"" + (48*60*60*1000) + "\">Every two days</option>" +
"<option value=\"" + -1 + "\">Never</option>" +
"</select>";
}
public String getUpdatePolicySelectBox() {
return "<select name=\"updatePolicy\">" +
"<option value=\"notify\">Notify only</option>" +
"<option value=\"download\">Download but don't install</option>" +
"<option value=\"install\">Install</option>" +
"</select>";
}
public String getTrustedKeys() {
StringBuffer buf = new StringBuffer(1024);
TrustedUpdate up = new TrustedUpdate(_context);
List keys = up.getTrustedKeys();
for (int i = 0; i < keys.size(); i++)
buf.append((String)keys.get(i)).append('\n');
return buf.toString();
}
}

View File

@ -96,7 +96,7 @@ public class SummaryHelper {
public boolean allowReseed() {
return (_context.netDb().getKnownRouters() < 10);
}
/**
* Retrieve amount of used memory.
*
@ -467,4 +467,6 @@ public class SummaryHelper {
return _context.throttle().getTunnelLag() + "ms";
}
public boolean updateAvailable() { return true; }
}

View File

@ -0,0 +1,157 @@
package net.i2p.router.web;
import java.io.File;
import java.text.DecimalFormat;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
import net.i2p.util.EepGet;
import net.i2p.util.Log;
/**
* Handle the request to update the router by firing off an EepGet call and
* displaying its status to anyone who asks. After the download completes,
* it is verified with the TrustedUpdate, and if it is authentic, the router
* is restarted.
*
*/
public class UpdateHandler {
private static UpdateRunner _updateRunner;
private RouterContext _context;
private Log _log;
private DecimalFormat _pct = new DecimalFormat("00.0%");
private static final String SIGNED_UPDATE_FILE = "i2pupdate.sud";
/**
* Configure this bean to query a particular router context
*
* @param contextId begging few characters of the routerHash, or null to pick
* the first one we come across.
*/
public void setContextId(String contextId) {
try {
_context = ContextHelper.getContext(contextId);
_log = _context.logManager().getLog(UpdateHandler.class);
} catch (Throwable t) {
t.printStackTrace();
}
}
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"))) {
synchronized (UpdateHandler.class) {
if (_updateRunner == null)
_updateRunner = new UpdateRunner();
if (_updateRunner.isRunning()) {
return;
} else {
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "true");
I2PThread update = new I2PThread(_updateRunner, "Update");
update.start();
}
}
}
}
public String getStatus() {
return _updateRunner.getStatus();
}
public class UpdateRunner implements Runnable, EepGet.StatusListener {
private boolean _isRunning;
private String _status;
private long _startedOn;
private long _written;
public UpdateRunner() {
_isRunning = false;
_status = "<b>Updating</b><br />";
}
public boolean isRunning() { return _isRunning; }
public String getStatus() { return _status; }
public void run() {
_isRunning = true;
update();
System.setProperty("net.i2p.router.web.ReseedHandler.updateInProgress", "false");
_isRunning = false;
}
private void update() {
_startedOn = -1;
_status = "<b>Updating</b><br />";
String updateURL = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL, ConfigUpdateHandler.DEFAULT_UPDATE_URL);
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);
String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT);
int proxyPort = -1;
try {
proxyPort = Integer.parseInt(port);
} catch (NumberFormatException nfe) {
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
return;
}
try {
EepGet get = null;
if (shouldProxy)
get = new EepGet(_context, proxyHost, proxyPort, 10, SIGNED_UPDATE_FILE, updateURL);
else
get = new EepGet(_context, 10, SIGNED_UPDATE_FILE, updateURL);
get.addStatusListener(UpdateRunner.this);
_startedOn = _context.clock().now();
get.fetch();
} catch (Throwable t) {
_context.logManager().getLog(UpdateHandler.class).error("Error updating", t);
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
}
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Attempt failed on " + url, cause);
_written = 0;
// ignored
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
_written += currentWrite;
StringBuffer buf = new StringBuffer(64);
buf.append("<b>Updating</b> ");
double pct = ((double)alreadyTransferred + (double)_written) / ((double)alreadyTransferred + (double)bytesRemaining);
synchronized (_pct) {
buf.append(_pct.format(pct));
}
buf.append(":<br />\n").append(_written+alreadyTransferred);
buf.append(" transferred<br />");
_status = buf.toString();
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
_status = "<b>Update downloaded</b><br />";
TrustedUpdate up = new TrustedUpdate(_context);
boolean ok = up.migrateVerified(SIGNED_UPDATE_FILE, "i2pupdate.zip");
File f = new File(SIGNED_UPDATE_FILE);
f.delete();
if (ok) {
_log.log(Log.CRIT, "Update was VERIFIED, restarting to install it");
_status = "<b>Update verified</b><br />Restarting<br />";
restart();
} else {
_log.log(Log.CRIT, "Update was INVALID - have you changed your keys?");
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_log.log(Log.CRIT, "Update did not download completely (" + bytesTransferred + " with "
+ bytesRemaining + " after " + currentAttempt + " tries)");
_status = "<b>Transfer failed</b><br />";
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
}
}
private void restart() {
_context.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART));
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
}
}