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 = "Updating
";
}
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 = "Updating
";
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("Updating ");
double pct = ((double)alreadyTransferred + (double)_written) / ((double)alreadyTransferred + (double)bytesRemaining);
synchronized (_pct) {
buf.append(_pct.format(pct));
}
buf.append(":
\n").append(_written+alreadyTransferred);
buf.append(" transferred
");
_status = buf.toString();
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
_status = "Update downloaded
";
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 = "Update verified
Restarting
";
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 = "Transfer failed
";
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);
}
}