* Update:

- Fix problems where a requested unsigned update would actually
        kick off a signed update
      - Fix problem when policy set to notify, and clicking
        check for update, incorrectly causing unsigned update download
        and bad messages
      - Verify zip integrity of unsigned updates
      - Move zip files to router dir, not base dir
      - More tweaks and cleanup
This commit is contained in:
zzz
2009-08-19 20:20:25 +00:00
parent 5bc2dab1d2
commit 6f053287d5
6 changed files with 103 additions and 34 deletions

View File

@ -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");

View File

@ -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();
}

View File

@ -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) {
File updFile = new File(_updateFile);
if (FileUtil.verifyZip(updFile)) {
_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();
} 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();

View File

@ -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);

View File

@ -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");
}
}

View File

@ -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.