forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.test2' (head 63cdcb547c0d33cd3c3c899b168ffab9f7ed2ebe)
to branch 'i2p.i2p' (head 52964ce47701fd8838e3f9c84af29d2463c83bc7)
This commit is contained in:
@ -16,6 +16,8 @@ public class ConfigUpdateHandler extends FormHandler {
|
|||||||
private String _proxyPort;
|
private String _proxyPort;
|
||||||
private boolean _updateThroughProxy;
|
private boolean _updateThroughProxy;
|
||||||
private String _trustedKeys;
|
private String _trustedKeys;
|
||||||
|
private boolean _updateUnsigned;
|
||||||
|
private String _zipURL;
|
||||||
|
|
||||||
public static final String PROP_NEWS_URL = "router.newsURL";
|
public static final String PROP_NEWS_URL = "router.newsURL";
|
||||||
// public static final String DEFAULT_NEWS_URL = "http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/news.xml?rev=HEAD";
|
// public static final String DEFAULT_NEWS_URL = "http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/news.xml?rev=HEAD";
|
||||||
@ -30,7 +32,12 @@ public class ConfigUpdateHandler extends FormHandler {
|
|||||||
public static final String PROP_PROXY_HOST = "router.updateProxyHost";
|
public static final String PROP_PROXY_HOST = "router.updateProxyHost";
|
||||||
public static final String DEFAULT_PROXY_HOST = "127.0.0.1";
|
public static final String DEFAULT_PROXY_HOST = "127.0.0.1";
|
||||||
public static final String PROP_PROXY_PORT = "router.updateProxyPort";
|
public static final String PROP_PROXY_PORT = "router.updateProxyPort";
|
||||||
public static final String DEFAULT_PROXY_PORT = "4444";
|
public static final int DEFAULT_PROXY_PORT_INT = 4444;
|
||||||
|
public static final String DEFAULT_PROXY_PORT = "" + DEFAULT_PROXY_PORT_INT;
|
||||||
|
/** default false */
|
||||||
|
public static final String PROP_UPDATE_UNSIGNED = "router.updateUnsigned";
|
||||||
|
/** no default */
|
||||||
|
public static final String PROP_ZIP_URL = "router.updateUnsignedURL";
|
||||||
|
|
||||||
public static final String PROP_UPDATE_URL = "router.updateURL";
|
public static final String PROP_UPDATE_URL = "router.updateURL";
|
||||||
public static final String DEFAULT_UPDATE_URL =
|
public static final String DEFAULT_UPDATE_URL =
|
||||||
@ -46,7 +53,9 @@ public class ConfigUpdateHandler extends FormHandler {
|
|||||||
if ("Check for update now".equals(_action)) {
|
if ("Check for update now".equals(_action)) {
|
||||||
NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext());
|
NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext());
|
||||||
fetcher.fetchNews();
|
fetcher.fetchNews();
|
||||||
if (fetcher.updateAvailable()) {
|
if (fetcher.shouldFetchUnsigned())
|
||||||
|
fetcher.fetchUnsigned();
|
||||||
|
if (fetcher.updateAvailable() || fetcher.unsignedUpdateAvailable()) {
|
||||||
if ( (_updatePolicy == null) || (!_updatePolicy.equals("notify")) )
|
if ( (_updatePolicy == null) || (!_updatePolicy.equals("notify")) )
|
||||||
addFormNotice("Update available, attempting to download now");
|
addFormNotice("Update available, attempting to download now");
|
||||||
else
|
else
|
||||||
@ -79,11 +88,8 @@ public class ConfigUpdateHandler extends FormHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_updateThroughProxy) {
|
_context.router().setConfigSetting(PROP_SHOULD_PROXY, "" + _updateThroughProxy);
|
||||||
_context.router().setConfigSetting(PROP_SHOULD_PROXY, Boolean.TRUE.toString());
|
_context.router().setConfigSetting(PROP_UPDATE_UNSIGNED, "" + _updateUnsigned);
|
||||||
} else {
|
|
||||||
_context.router().setConfigSetting(PROP_SHOULD_PROXY, Boolean.FALSE.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
String oldFreqStr = _context.router().getConfigSetting(PROP_REFRESH_FREQUENCY);
|
String oldFreqStr = _context.router().getConfigSetting(PROP_REFRESH_FREQUENCY);
|
||||||
long oldFreq = -1;
|
long oldFreq = -1;
|
||||||
@ -119,6 +125,14 @@ public class ConfigUpdateHandler extends FormHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( (_zipURL != null) && (_zipURL.length() > 0) ) {
|
||||||
|
String oldURL = _context.router().getConfigSetting(PROP_ZIP_URL);
|
||||||
|
if ( (oldURL == null) || (!_zipURL.equals(oldURL)) ) {
|
||||||
|
_context.router().setConfigSetting(PROP_ZIP_URL, _zipURL);
|
||||||
|
addFormNotice("Updating unsigned update URL to " + _zipURL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_context.router().saveConfig();
|
_context.router().saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,4 +146,6 @@ public class ConfigUpdateHandler extends FormHandler {
|
|||||||
public void setUpdateThroughProxy(String foo) { _updateThroughProxy = true; }
|
public void setUpdateThroughProxy(String foo) { _updateThroughProxy = true; }
|
||||||
public void setProxyHost(String host) { _proxyHost = host; }
|
public void setProxyHost(String host) { _proxyHost = host; }
|
||||||
public void setProxyPort(String port) { _proxyPort = port; }
|
public void setProxyPort(String port) { _proxyPort = port; }
|
||||||
|
public void setUpdateUnsigned(String foo) { _updateUnsigned = true; }
|
||||||
|
public void setZipURL(String url) { _zipURL = url; }
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,17 @@ public class ConfigUpdateHelper extends HelperBase {
|
|||||||
if (Boolean.valueOf(proxy).booleanValue())
|
if (Boolean.valueOf(proxy).booleanValue())
|
||||||
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateThroughProxy\" checked=\"true\" >";
|
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateThroughProxy\" checked=\"true\" >";
|
||||||
else
|
else
|
||||||
|
|
||||||
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateThroughProxy\" >";
|
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateThroughProxy\" >";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUpdateUnsigned() {
|
||||||
|
String foo = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_UNSIGNED);
|
||||||
|
if (Boolean.valueOf(foo).booleanValue())
|
||||||
|
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateUnsigned\" checked=\"true\" >";
|
||||||
|
else
|
||||||
|
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateUnsigned\" >";
|
||||||
|
}
|
||||||
|
|
||||||
private static final long PERIODS[] = new long[] { 12*60*60*1000l, 24*60*60*1000l, 48*60*60*1000l, -1l };
|
private static final long PERIODS[] = new long[] { 12*60*60*1000l, 24*60*60*1000l, 48*60*60*1000l, -1l };
|
||||||
|
|
||||||
public String getRefreshFrequencySelectBox() {
|
public String getRefreshFrequencySelectBox() {
|
||||||
@ -105,11 +112,11 @@ public class ConfigUpdateHelper extends HelperBase {
|
|||||||
return new TrustedUpdate(_context).getTrustedKeysString();
|
return new TrustedUpdate(_context).getTrustedKeysString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getZipURL() {
|
||||||
|
return _context.getProperty(ConfigUpdateHandler.PROP_ZIP_URL, "");
|
||||||
|
}
|
||||||
|
|
||||||
public String getNewsStatus() {
|
public String getNewsStatus() {
|
||||||
return NewsFetcher.getInstance(_context).status();
|
return NewsFetcher.getInstance(_context).status();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUpdateVersion() {
|
|
||||||
return NewsFetcher.getInstance(_context).updateVersion();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,11 @@ package net.i2p.router.web;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.crypto.TrustedUpdate;
|
import net.i2p.crypto.TrustedUpdate;
|
||||||
@ -12,6 +16,7 @@ import net.i2p.router.Router;
|
|||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.RouterVersion;
|
import net.i2p.router.RouterVersion;
|
||||||
import net.i2p.util.EepGet;
|
import net.i2p.util.EepGet;
|
||||||
|
import net.i2p.util.EepHead;
|
||||||
import net.i2p.util.FileUtil;
|
import net.i2p.util.FileUtil;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@ -23,9 +28,11 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
private I2PAppContext _context;
|
private I2PAppContext _context;
|
||||||
private Log _log;
|
private Log _log;
|
||||||
private boolean _updateAvailable;
|
private boolean _updateAvailable;
|
||||||
|
private boolean _unsignedUpdateAvailable;
|
||||||
private long _lastFetch;
|
private long _lastFetch;
|
||||||
private long _lastUpdated;
|
private long _lastUpdated;
|
||||||
private String _updateVersion;
|
private String _updateVersion;
|
||||||
|
private String _unsignedUpdateVersion;
|
||||||
private String _lastModified;
|
private String _lastModified;
|
||||||
private File _newsFile;
|
private File _newsFile;
|
||||||
private File _tempFile;
|
private File _tempFile;
|
||||||
@ -63,6 +70,8 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
|
|
||||||
public boolean updateAvailable() { return _updateAvailable; }
|
public boolean updateAvailable() { return _updateAvailable; }
|
||||||
public String updateVersion() { return _updateVersion; }
|
public String updateVersion() { return _updateVersion; }
|
||||||
|
public boolean unsignedUpdateAvailable() { return _unsignedUpdateAvailable; }
|
||||||
|
public String unsignedUpdateVersion() { return _unsignedUpdateVersion; }
|
||||||
|
|
||||||
public String status() {
|
public String status() {
|
||||||
long now = _context.clock().now();
|
long now = _context.clock().now();
|
||||||
@ -75,8 +84,11 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
try { Thread.sleep(_context.random().nextLong(5*60*1000)); } catch (InterruptedException ie) {}
|
try { Thread.sleep(_context.random().nextLong(5*60*1000)); } catch (InterruptedException ie) {}
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!_updateAvailable) checkForUpdates();
|
if (!_updateAvailable) checkForUpdates();
|
||||||
if (shouldFetchNews())
|
if (shouldFetchNews()) {
|
||||||
fetchNews();
|
fetchNews();
|
||||||
|
if (shouldFetchUnsigned())
|
||||||
|
fetchUnsignedHead();
|
||||||
|
}
|
||||||
try { Thread.sleep(10*60*1000); } catch (InterruptedException ie) {}
|
try { Thread.sleep(10*60*1000); } catch (InterruptedException ie) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,9 +103,8 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
|
|
||||||
private boolean shouldFetchNews() {
|
private boolean shouldFetchNews() {
|
||||||
updateLastFetched();
|
updateLastFetched();
|
||||||
String freq = _context.getProperty(ConfigUpdateHandler.PROP_REFRESH_FREQUENCY);
|
String freq = _context.getProperty(ConfigUpdateHandler.PROP_REFRESH_FREQUENCY,
|
||||||
if (freq == null)
|
ConfigUpdateHandler.DEFAULT_REFRESH_FREQUENCY);
|
||||||
freq = ConfigUpdateHandler.DEFAULT_REFRESH_FREQUENCY;
|
|
||||||
try {
|
try {
|
||||||
long ms = Long.parseLong(freq);
|
long ms = Long.parseLong(freq);
|
||||||
if (ms <= 0)
|
if (ms <= 0)
|
||||||
@ -116,13 +127,11 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
String newsURL = ConfigUpdateHelper.getNewsURL(_context);
|
String newsURL = ConfigUpdateHelper.getNewsURL(_context);
|
||||||
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
|
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 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 = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
|
||||||
if (_tempFile.exists())
|
if (_tempFile.exists())
|
||||||
_tempFile.delete();
|
_tempFile.delete();
|
||||||
|
|
||||||
int proxyPort = -1;
|
|
||||||
try {
|
try {
|
||||||
proxyPort = Integer.parseInt(port);
|
|
||||||
EepGet get = null;
|
EepGet get = null;
|
||||||
if (shouldProxy)
|
if (shouldProxy)
|
||||||
get = new EepGet(_context, true, proxyHost, proxyPort, 2, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified);
|
get = new EepGet(_context, true, proxyHost, proxyPort, 2, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified);
|
||||||
@ -136,6 +145,103 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean shouldFetchUnsigned() {
|
||||||
|
String url = _context.getProperty(ConfigUpdateHandler.PROP_ZIP_URL);
|
||||||
|
return url != null && url.length() > 0 &&
|
||||||
|
Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_UPDATE_UNSIGNED)).booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.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 = _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) {
|
||||||
|
if (!(_context instanceof RouterContext)) return;
|
||||||
|
long modtime = 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.
|
||||||
|
((RouterContext)_context).router().setConfigSetting(UpdateHandler.PROP_LAST_UPDATE_TIME,
|
||||||
|
"" + _context.clock().now());
|
||||||
|
((RouterContext)_context).router().saveConfig();
|
||||||
|
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' with month name in the system locale
|
||||||
|
_unsignedUpdateVersion = (new SimpleDateFormat("dd-MMM HH:mm")).format(new Date(modtime));
|
||||||
|
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((RouterContext)_context, url,
|
||||||
|
_unsignedUpdateVersion);
|
||||||
|
handler.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://jimyjoshi.com/blog/2007/08/rfc822dateparsinginjava.html
|
||||||
|
* Apparently public domain
|
||||||
|
* Probably don't need all of these...
|
||||||
|
*/
|
||||||
|
private static final SimpleDateFormat rfc822DateFormats[] = new SimpleDateFormat[] {
|
||||||
|
new SimpleDateFormat("EEE, d MMM yy HH:mm:ss z", Locale.US),
|
||||||
|
new SimpleDateFormat("EEE, d MMM yy HH:mm z", Locale.US),
|
||||||
|
new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US),
|
||||||
|
new SimpleDateFormat("EEE, d MMM yyyy HH:mm z", Locale.US),
|
||||||
|
new SimpleDateFormat("d MMM yy HH:mm z", Locale.US),
|
||||||
|
new SimpleDateFormat("d MMM yy HH:mm:ss z", Locale.US),
|
||||||
|
new SimpleDateFormat("d MMM yyyy HH:mm z", Locale.US),
|
||||||
|
new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* new Date(String foo) is deprecated, so let's do this the hard way
|
||||||
|
*
|
||||||
|
* @param s non-null
|
||||||
|
* @return -1 on failure
|
||||||
|
*/
|
||||||
|
public static long parse822Date(String s) {
|
||||||
|
for (int i = 0; i < rfc822DateFormats.length; i++) {
|
||||||
|
try {
|
||||||
|
Date date = rfc822DateFormats[i].parse(s);
|
||||||
|
if (date != null)
|
||||||
|
return date.getTime();
|
||||||
|
} catch (ParseException pe) {}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
private static final String VERSION_STRING = "version=\"" + RouterVersion.VERSION + "\"";
|
private static final String VERSION_STRING = "version=\"" + RouterVersion.VERSION + "\"";
|
||||||
private static final String VERSION_PREFIX = "version=\"";
|
private static final String VERSION_PREFIX = "version=\"";
|
||||||
private void checkForUpdates() {
|
private void checkForUpdates() {
|
||||||
|
@ -525,4 +525,15 @@ public class SummaryHelper extends HelperBase {
|
|||||||
return NewsFetcher.getInstance(_context).updateAvailable();
|
return NewsFetcher.getInstance(_context).updateAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean unsignedUpdateAvailable() {
|
||||||
|
return NewsFetcher.getInstance(_context).unsignedUpdateAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUpdateVersion() {
|
||||||
|
return NewsFetcher.getInstance(_context).updateVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnsignedUpdateVersion() {
|
||||||
|
return NewsFetcher.getInstance(_context).unsignedUpdateVersion();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
package net.i2p.router.web;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.router.Router;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.util.EepGet;
|
||||||
|
import net.i2p.util.FileUtil;
|
||||||
|
import net.i2p.util.I2PAppThread;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>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.
|
||||||
|
* </p>
|
||||||
|
* <p>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.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class UnsignedUpdateHandler extends UpdateHandler {
|
||||||
|
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, "false"))) {
|
||||||
|
_log.error("Update already running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (UpdateHandler.class) {
|
||||||
|
if (_updateRunner == null) {
|
||||||
|
_updateRunner = new UnsignedUpdateRunner();
|
||||||
|
}
|
||||||
|
if (_updateRunner.isRunning()) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
System.setProperty(PROP_UPDATE_IN_PROGRESS, "true");
|
||||||
|
I2PAppThread update = new I2PAppThread(_updateRunner, "Update");
|
||||||
|
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() {
|
||||||
|
_status = "<b>Updating</b>";
|
||||||
|
// 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 = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
|
||||||
|
try {
|
||||||
|
// 40 retries!!
|
||||||
|
_get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, _zipURL, false);
|
||||||
|
_get.addStatusListener(UnsignedUpdateRunner.this);
|
||||||
|
_get.fetch();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
_context.logManager().getLog(UpdateHandler.class).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();
|
||||||
|
boolean copied = FileUtil.copy(_updateFile, to, true);
|
||||||
|
if (copied) {
|
||||||
|
(new File(_updateFile)).delete();
|
||||||
|
String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY);
|
||||||
|
this.done = true;
|
||||||
|
String lastmod = _get.getLastModified();
|
||||||
|
long modtime = 0;
|
||||||
|
if (lastmod != null)
|
||||||
|
modtime = NewsFetcher.parse822Date(lastmod);
|
||||||
|
if (modtime <= 0)
|
||||||
|
modtime = _context.clock().now();
|
||||||
|
_context.router().setConfigSetting(PROP_LAST_UPDATE_TIME, "" + modtime);
|
||||||
|
_context.router().saveConfig();
|
||||||
|
if ("install".equals(policy)) {
|
||||||
|
_log.log(Log.CRIT, "Update was downloaded, restarting to install it");
|
||||||
|
_status = "<b>Update downloaded</b><br />Restarting";
|
||||||
|
restart();
|
||||||
|
} else {
|
||||||
|
_log.log(Log.CRIT, "Update was downloaded, will be installed at next restart");
|
||||||
|
_status = "<b>Update downloaded</b><br />";
|
||||||
|
if (System.getProperty("wrapper.version") != null)
|
||||||
|
_status += "Click Restart to install";
|
||||||
|
else
|
||||||
|
_status += "Click Shutdown and restart to install";
|
||||||
|
_status += " Version " + _zipVersion;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_log.log(Log.CRIT, "Failed copy to " + to);
|
||||||
|
_status = "<b>Failed copy to " + to + "</b>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,11 +30,12 @@ public class UpdateHandler {
|
|||||||
protected static UpdateRunner _updateRunner;
|
protected static UpdateRunner _updateRunner;
|
||||||
protected RouterContext _context;
|
protected RouterContext _context;
|
||||||
protected Log _log;
|
protected Log _log;
|
||||||
protected DecimalFormat _pct = new DecimalFormat("00.0%");
|
|
||||||
protected String _updateFile;
|
protected String _updateFile;
|
||||||
|
private String _action;
|
||||||
|
|
||||||
protected static final String SIGNED_UPDATE_FILE = "i2pupdate.sud";
|
protected static final String SIGNED_UPDATE_FILE = "i2pupdate.sud";
|
||||||
protected static final String PROP_UPDATE_IN_PROGRESS = "net.i2p.router.web.UpdateHandler.updateInProgress";
|
protected static final String PROP_UPDATE_IN_PROGRESS = "net.i2p.router.web.UpdateHandler.updateInProgress";
|
||||||
|
protected static final String PROP_LAST_UPDATE_TIME = "router.updateLastDownloaded";
|
||||||
|
|
||||||
public UpdateHandler() {
|
public UpdateHandler() {
|
||||||
this(ContextHelper.getContext(null));
|
this(ContextHelper.getContext(null));
|
||||||
@ -48,7 +49,7 @@ public class UpdateHandler {
|
|||||||
/**
|
/**
|
||||||
* Configure this bean to query a particular router context
|
* Configure this bean to query a particular router context
|
||||||
*
|
*
|
||||||
* @param contextId begging few characters of the routerHash, or null to pick
|
* @param contextId beginning few characters of the routerHash, or null to pick
|
||||||
* the first one we come across.
|
* the first one we come across.
|
||||||
*/
|
*/
|
||||||
public void setContextId(String contextId) {
|
public void setContextId(String contextId) {
|
||||||
@ -60,18 +61,25 @@ public class UpdateHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUpdateAction(String val) { _action = val; }
|
||||||
|
|
||||||
public void setUpdateNonce(String nonce) {
|
public void setUpdateNonce(String nonce) {
|
||||||
if (nonce == null) return;
|
if (nonce == null) return;
|
||||||
if (nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.nonce")) ||
|
if (nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.nonce")) ||
|
||||||
nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.noncePrev"))) {
|
nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.noncePrev"))) {
|
||||||
update();
|
if (_action != null && _action.contains("Unsigned")) {
|
||||||
|
// Not us, have NewsFetcher instantiate the correct class.
|
||||||
|
NewsFetcher fetcher = NewsFetcher.getInstance(_context);
|
||||||
|
fetcher.fetchUnsigned();
|
||||||
|
} else {
|
||||||
|
update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
// don't block waiting for the other one to finish
|
// 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");
|
_log.error("Update already running");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -106,6 +114,9 @@ public class UpdateHandler {
|
|||||||
protected boolean _isRunning;
|
protected boolean _isRunning;
|
||||||
protected boolean done;
|
protected boolean done;
|
||||||
protected String _status;
|
protected String _status;
|
||||||
|
protected EepGet _get;
|
||||||
|
private final DecimalFormat _pct = new DecimalFormat("0.0%");
|
||||||
|
|
||||||
public UpdateRunner() {
|
public UpdateRunner() {
|
||||||
_isRunning = false;
|
_isRunning = false;
|
||||||
this.done = false;
|
this.done = false;
|
||||||
@ -129,22 +140,15 @@ public class UpdateHandler {
|
|||||||
_log.debug("Selected update URL: " + updateURL);
|
_log.debug("Selected update URL: " + updateURL);
|
||||||
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
|
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 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 = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
|
||||||
int proxyPort = -1;
|
|
||||||
try {
|
try {
|
||||||
proxyPort = Integer.parseInt(port);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
EepGet get = null;
|
|
||||||
if (shouldProxy)
|
if (shouldProxy)
|
||||||
// 40 retries!!
|
// 40 retries!!
|
||||||
get = new EepGet(_context, proxyHost, proxyPort, 40, SIGNED_UPDATE_FILE, updateURL, false);
|
_get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false);
|
||||||
else
|
else
|
||||||
get = new EepGet(_context, 1, _updateFile, updateURL, false);
|
_get = new EepGet(_context, 1, _updateFile, updateURL, false);
|
||||||
get.addStatusListener(UpdateRunner.this);
|
_get.addStatusListener(UpdateRunner.this);
|
||||||
get.fetch();
|
_get.fetch();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
_context.logManager().getLog(UpdateHandler.class).error("Error updating", t);
|
_context.logManager().getLog(UpdateHandler.class).error("Error updating", t);
|
||||||
}
|
}
|
||||||
@ -177,6 +181,15 @@ public class UpdateHandler {
|
|||||||
if (err == null) {
|
if (err == null) {
|
||||||
String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY);
|
String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY);
|
||||||
this.done = true;
|
this.done = true;
|
||||||
|
// So unsigned update handler doesn't overwrite unless newer.
|
||||||
|
String lastmod = _get.getLastModified();
|
||||||
|
long modtime = 0;
|
||||||
|
if (lastmod != null)
|
||||||
|
modtime = NewsFetcher.parse822Date(lastmod);
|
||||||
|
if (modtime <= 0)
|
||||||
|
modtime = _context.clock().now();
|
||||||
|
_context.router().setConfigSetting(PROP_LAST_UPDATE_TIME, "" + modtime);
|
||||||
|
_context.router().saveConfig();
|
||||||
if ("install".equals(policy)) {
|
if ("install".equals(policy)) {
|
||||||
_log.log(Log.CRIT, "Update was VERIFIED, restarting to install it");
|
_log.log(Log.CRIT, "Update was VERIFIED, restarting to install it");
|
||||||
_status = "<b>Update verified</b><br />Restarting";
|
_status = "<b>Update verified</b><br />Restarting";
|
||||||
@ -208,7 +221,7 @@ public class UpdateHandler {
|
|||||||
public void attempting(String url) {}
|
public void attempting(String url) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restart() {
|
protected void restart() {
|
||||||
_context.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART));
|
_context.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART));
|
||||||
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
|
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,12 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class= "mediumtags" align="right"><b>Trusted keys:</b>
|
<td class= "mediumtags" align="right"><b>Trusted keys:</b>
|
||||||
<td><textarea name="trustedKeys" wrap="off"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea>
|
<td><textarea name="trustedKeys" wrap="off"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea>
|
||||||
|
<tr>
|
||||||
|
<td class= "mediumtags" align="right"><b>Update with unsigned development builds?</b>
|
||||||
|
<td><jsp:getProperty name="updatehelper" property="updateUnsigned" />
|
||||||
|
<tr>
|
||||||
|
<td class= "mediumtags" align="right"><b>Unsigned Build URL:</b></td>
|
||||||
|
<td><input type="text" size="60" name="zipURL" value="<jsp:getProperty name="updatehelper" property="zipURL" />"></td>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<td><div class="formaction">
|
<td><div class="formaction">
|
||||||
|
@ -12,11 +12,8 @@
|
|||||||
<jsp:useBean class="net.i2p.router.web.UpdateHandler" id="update" scope="request" />
|
<jsp:useBean class="net.i2p.router.web.UpdateHandler" id="update" scope="request" />
|
||||||
<jsp:setProperty name="update" property="*" />
|
<jsp:setProperty name="update" property="*" />
|
||||||
<jsp:setProperty name="update" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
<jsp:setProperty name="update" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||||
<jsp:useBean class="net.i2p.router.web.ConfigUpdateHelper" id="uhelper" scope="request" />
|
<center><a href="index.jsp" target="_top"><img src="/themes/console/images/i2plogo.png" alt="I2P Router Console" title="I2P Router Console"/></a></center><hr />
|
||||||
<jsp:setProperty name="uhelper" property="*" />
|
<center>
|
||||||
<jsp:setProperty name="uhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
|
||||||
<a href="index.jsp" target="_top"><img src="/themes/console/images/i2plogo.png" alt="I2P Router Console" title="I2P Router Console"/></a><hr />
|
|
||||||
|
|
||||||
<% java.io.File lpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getBaseDir(), "docs/toolbar.html");
|
<% java.io.File lpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getBaseDir(), "docs/toolbar.html");
|
||||||
// you better have target="_top" for the links in there...
|
// you better have target="_top" for the links in there...
|
||||||
if (lpath.exists()) { %>
|
if (lpath.exists()) { %>
|
||||||
@ -63,7 +60,7 @@
|
|||||||
<hr><h4><a href="config.jsp#help" target="_top" title="Help with configuring your firewall and router for optimal I2P performance."><jsp:getProperty name="helper" property="reachability" /></a></h4>
|
<hr><h4><a href="config.jsp#help" target="_top" title="Help with configuring your firewall and router for optimal I2P performance."><jsp:getProperty name="helper" property="reachability" /></a></h4>
|
||||||
<hr>
|
<hr>
|
||||||
<%
|
<%
|
||||||
if (helper.updateAvailable()) {
|
if (helper.updateAvailable() || helper.unsignedUpdateAvailable()) {
|
||||||
// display all the time so we display the final failure message
|
// display all the time so we display the final failure message
|
||||||
out.print("<br />" + update.getStatus());
|
out.print("<br />" + update.getStatus());
|
||||||
if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress"))) {
|
if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress"))) {
|
||||||
@ -78,7 +75,11 @@
|
|||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
out.print("<p><form action=\"" + uri + "\" method=\"GET\">\n");
|
out.print("<p><form action=\"" + uri + "\" method=\"GET\">\n");
|
||||||
out.print("<input type=\"hidden\" name=\"updateNonce\" value=\"" + nonce + "\" />\n");
|
out.print("<input type=\"hidden\" name=\"updateNonce\" value=\"" + nonce + "\" />\n");
|
||||||
out.print("<input type=\"submit\" value=\"Download " + uhelper.getUpdateVersion() + " Update\" /></form></p>\n");
|
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("</form></center></p>\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
|
@ -23,48 +23,50 @@ import net.i2p.data.DataHelper;
|
|||||||
* [-o outputFile]
|
* [-o outputFile]
|
||||||
* [-m markSize lineLen]
|
* [-m markSize lineLen]
|
||||||
* url
|
* url
|
||||||
|
*
|
||||||
|
* Bug: a malformed url http://example.i2p (no trailing '/') fails cryptically
|
||||||
*/
|
*/
|
||||||
public class EepGet {
|
public class EepGet {
|
||||||
private I2PAppContext _context;
|
private I2PAppContext _context;
|
||||||
private Log _log;
|
protected Log _log;
|
||||||
private boolean _shouldProxy;
|
protected boolean _shouldProxy;
|
||||||
private String _proxyHost;
|
private String _proxyHost;
|
||||||
private int _proxyPort;
|
private int _proxyPort;
|
||||||
private int _numRetries;
|
protected int _numRetries;
|
||||||
private long _minSize; // minimum and maximum acceptable response size, -1 signifies unlimited,
|
private long _minSize; // minimum and maximum acceptable response size, -1 signifies unlimited,
|
||||||
private long _maxSize; // applied both against whole responses and chunks
|
private long _maxSize; // applied both against whole responses and chunks
|
||||||
private String _outputFile;
|
private String _outputFile;
|
||||||
private OutputStream _outputStream;
|
private OutputStream _outputStream;
|
||||||
/** url we were asked to fetch */
|
/** url we were asked to fetch */
|
||||||
private String _url;
|
protected String _url;
|
||||||
/** the URL we actually fetch from (may differ from the _url in case of redirect) */
|
/** the URL we actually fetch from (may differ from the _url in case of redirect) */
|
||||||
private String _actualURL;
|
protected String _actualURL;
|
||||||
private String _postData;
|
private String _postData;
|
||||||
private boolean _allowCaching;
|
private boolean _allowCaching;
|
||||||
private List _listeners;
|
protected List _listeners;
|
||||||
|
|
||||||
private boolean _keepFetching;
|
private boolean _keepFetching;
|
||||||
private Socket _proxy;
|
private Socket _proxy;
|
||||||
private OutputStream _proxyOut;
|
private OutputStream _proxyOut;
|
||||||
private InputStream _proxyIn;
|
private InputStream _proxyIn;
|
||||||
private OutputStream _out;
|
protected OutputStream _out;
|
||||||
private long _alreadyTransferred;
|
private long _alreadyTransferred;
|
||||||
private long _bytesTransferred;
|
private long _bytesTransferred;
|
||||||
private long _bytesRemaining;
|
protected long _bytesRemaining;
|
||||||
private int _currentAttempt;
|
protected int _currentAttempt;
|
||||||
private String _etag;
|
private String _etag;
|
||||||
private String _lastModified;
|
private String _lastModified;
|
||||||
private boolean _encodingChunked;
|
private boolean _encodingChunked;
|
||||||
private boolean _notModified;
|
private boolean _notModified;
|
||||||
private String _contentType;
|
private String _contentType;
|
||||||
private boolean _transferFailed;
|
protected boolean _transferFailed;
|
||||||
private boolean _headersRead;
|
protected boolean _headersRead;
|
||||||
private boolean _aborted;
|
protected boolean _aborted;
|
||||||
private long _fetchHeaderTimeout;
|
private long _fetchHeaderTimeout;
|
||||||
private long _fetchEndTime;
|
private long _fetchEndTime;
|
||||||
private long _fetchInactivityTimeout;
|
protected long _fetchInactivityTimeout;
|
||||||
private int _redirects;
|
protected int _redirects;
|
||||||
private String _redirectLocation;
|
protected String _redirectLocation;
|
||||||
|
|
||||||
public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) {
|
public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) {
|
||||||
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url);
|
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url);
|
||||||
@ -214,7 +216,7 @@ public class EepGet {
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void usage() {
|
protected static void usage() {
|
||||||
System.err.println("EepGet [-p 127.0.0.1:4444] [-n #retries] [-o outputFile] [-m markSize lineLen] [-t timeout] url");
|
System.err.println("EepGet [-p 127.0.0.1:4444] [-n #retries] [-o outputFile] [-m markSize lineLen] [-t timeout] url");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,7 +482,7 @@ public class EepGet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** return true if the URL was completely retrieved */
|
/** return true if the URL was completely retrieved */
|
||||||
private void doFetch(SocketTimeout timeout) throws IOException {
|
protected void doFetch(SocketTimeout timeout) throws IOException {
|
||||||
_headersRead = false;
|
_headersRead = false;
|
||||||
_aborted = false;
|
_aborted = false;
|
||||||
try {
|
try {
|
||||||
@ -625,7 +627,7 @@ public class EepGet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readHeaders() throws IOException {
|
protected void readHeaders() throws IOException {
|
||||||
String key = null;
|
String key = null;
|
||||||
StringBuilder buf = new StringBuilder(32);
|
StringBuilder buf = new StringBuilder(32);
|
||||||
|
|
||||||
@ -844,7 +846,7 @@ public class EepGet {
|
|||||||
private static final byte NL = '\n';
|
private static final byte NL = '\n';
|
||||||
private boolean isNL(byte b) { return (b == NL); }
|
private boolean isNL(byte b) { return (b == NL); }
|
||||||
|
|
||||||
private void sendRequest(SocketTimeout timeout) throws IOException {
|
protected void sendRequest(SocketTimeout timeout) throws IOException {
|
||||||
if (_outputStream != null) {
|
if (_outputStream != null) {
|
||||||
// We are reading into a stream supplied by a caller,
|
// We are reading into a stream supplied by a caller,
|
||||||
// for which we cannot easily determine how much we've written.
|
// for which we cannot easily determine how much we've written.
|
||||||
@ -892,7 +894,7 @@ public class EepGet {
|
|||||||
_log.debug("Request flushed");
|
_log.debug("Request flushed");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRequest() throws IOException {
|
protected String getRequest() throws IOException {
|
||||||
StringBuilder buf = new StringBuilder(512);
|
StringBuilder buf = new StringBuilder(512);
|
||||||
boolean post = false;
|
boolean post = false;
|
||||||
if ( (_postData != null) && (_postData.length() > 0) )
|
if ( (_postData != null) && (_postData.length() > 0) )
|
||||||
@ -963,5 +965,4 @@ public class EepGet {
|
|||||||
public String getContentType() {
|
public String getContentType() {
|
||||||
return _contentType;
|
return _contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
204
core/java/src/net/i2p/util/EepHead.java
Normal file
204
core/java/src/net/i2p/util/EepHead.java
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
package net.i2p.util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a quick hack to get a working EepHead, primarily for the following usage:
|
||||||
|
*
|
||||||
|
* EepHead foo = new EepHead(...);
|
||||||
|
* if (foo.fetch()) {
|
||||||
|
* String lastmod = foo.getLastModified();
|
||||||
|
* if (lastmod != null) {
|
||||||
|
* parse the string...
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Other use cases (command line, listeners, etc...) lightly- or un-tested.
|
||||||
|
*
|
||||||
|
* Writing from scratch rather than extending EepGet would maybe have been less bloated memory-wise.
|
||||||
|
* This way gets us redirect handling, among other benefits.
|
||||||
|
*
|
||||||
|
* @author zzz
|
||||||
|
*/
|
||||||
|
public class EepHead extends EepGet {
|
||||||
|
/** EepGet needs either a non-null file or a stream... shouldn't actually be written to... */
|
||||||
|
static final OutputStream _dummyStream = new ByteArrayOutputStream(0);
|
||||||
|
|
||||||
|
public EepHead(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String url) {
|
||||||
|
// we're using this constructor:
|
||||||
|
// public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
|
||||||
|
super(ctx, true, proxyHost, proxyPort, numRetries, -1, -1, null, _dummyStream, url, true, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EepHead [-p 127.0.0.1:4444] [-n #retries] url
|
||||||
|
*
|
||||||
|
* This doesn't really do much since it doesn't register a listener.
|
||||||
|
* EepGet doesn't have a method to store and return all the headers, so just print
|
||||||
|
* out the ones we have methods for.
|
||||||
|
* Turn on logging to use it for a decent test.
|
||||||
|
*/
|
||||||
|
public static void main(String args[]) {
|
||||||
|
String proxyHost = "127.0.0.1";
|
||||||
|
int proxyPort = 4444;
|
||||||
|
int numRetries = 0;
|
||||||
|
int inactivityTimeout = 60*1000;
|
||||||
|
String url = null;
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (args[i].equals("-p")) {
|
||||||
|
proxyHost = args[i+1].substring(0, args[i+1].indexOf(':'));
|
||||||
|
String port = args[i+1].substring(args[i+1].indexOf(':')+1);
|
||||||
|
proxyPort = Integer.parseInt(port);
|
||||||
|
i++;
|
||||||
|
} else if (args[i].equals("-n")) {
|
||||||
|
numRetries = Integer.parseInt(args[i+1]);
|
||||||
|
i++;
|
||||||
|
} else if (args[i].equals("-t")) {
|
||||||
|
inactivityTimeout = 1000 * Integer.parseInt(args[i+1]);
|
||||||
|
i++;
|
||||||
|
} else if (args[i].startsWith("-")) {
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
url = args[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url == null) {
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EepHead get = new EepHead(I2PAppContext.getGlobalContext(), proxyHost, proxyPort, numRetries, url);
|
||||||
|
if (get.fetch(45*1000, -1, inactivityTimeout)) {
|
||||||
|
System.err.println("Content-Type: " + get.getContentType());
|
||||||
|
System.err.println("Content-Length: " + get.getContentLength());
|
||||||
|
System.err.println("Last-Modified: " + get.getLastModified());
|
||||||
|
System.err.println("Etag: " + get.getETag());
|
||||||
|
} else {
|
||||||
|
System.err.println("Failed " + url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void usage() {
|
||||||
|
System.err.println("EepHead [-p 127.0.0.1:4444] [-n #retries] [-t timeout] url");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** return true if the URL was completely retrieved */
|
||||||
|
@Override
|
||||||
|
protected void doFetch(SocketTimeout timeout) throws IOException {
|
||||||
|
_headersRead = false;
|
||||||
|
_aborted = false;
|
||||||
|
try {
|
||||||
|
readHeaders();
|
||||||
|
} finally {
|
||||||
|
_headersRead = true;
|
||||||
|
}
|
||||||
|
if (_aborted)
|
||||||
|
throw new IOException("Timed out reading the HTTP headers");
|
||||||
|
|
||||||
|
timeout.resetTimer();
|
||||||
|
if (_fetchInactivityTimeout > 0)
|
||||||
|
timeout.setInactivityTimeout(_fetchInactivityTimeout);
|
||||||
|
else
|
||||||
|
timeout.setInactivityTimeout(60*1000);
|
||||||
|
|
||||||
|
if (_redirectLocation != null) {
|
||||||
|
try {
|
||||||
|
URL oldURL = new URL(_actualURL);
|
||||||
|
String query = oldURL.getQuery();
|
||||||
|
if (query == null) query = "";
|
||||||
|
if (_redirectLocation.startsWith("http://")) {
|
||||||
|
if ( (_redirectLocation.indexOf('?') < 0) && (query.length() > 0) )
|
||||||
|
_actualURL = _redirectLocation + "?" + query;
|
||||||
|
else
|
||||||
|
_actualURL = _redirectLocation;
|
||||||
|
} else {
|
||||||
|
URL url = new URL(_actualURL);
|
||||||
|
if (_redirectLocation.startsWith("/"))
|
||||||
|
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + _redirectLocation;
|
||||||
|
else
|
||||||
|
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + "/" + _redirectLocation;
|
||||||
|
if ( (_actualURL.indexOf('?') < 0) && (query.length() > 0) )
|
||||||
|
_actualURL = _actualURL + "?" + query;
|
||||||
|
}
|
||||||
|
} catch (MalformedURLException mue) {
|
||||||
|
throw new IOException("Redirected from an invalid URL");
|
||||||
|
}
|
||||||
|
_redirects++;
|
||||||
|
if (_redirects > 5)
|
||||||
|
throw new IOException("Too many redirects: to " + _redirectLocation);
|
||||||
|
if (_log.shouldLog(Log.INFO)) _log.info("Redirecting to " + _redirectLocation);
|
||||||
|
sendRequest(timeout);
|
||||||
|
doFetch(timeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Headers read completely");
|
||||||
|
|
||||||
|
if (_out != null)
|
||||||
|
_out.close();
|
||||||
|
_out = null;
|
||||||
|
|
||||||
|
if (_aborted)
|
||||||
|
throw new IOException("Timed out reading the HTTP data");
|
||||||
|
|
||||||
|
timeout.cancel();
|
||||||
|
|
||||||
|
if (_transferFailed) {
|
||||||
|
// 404, etc - transferFailed is called after all attempts fail, by fetch() above
|
||||||
|
for (int i = 0; i < _listeners.size(); i++)
|
||||||
|
((StatusListener)_listeners.get(i)).attemptFailed(_url, 0, 0, _currentAttempt, _numRetries, new Exception("Attempt failed"));
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < _listeners.size(); i++)
|
||||||
|
((StatusListener)_listeners.get(i)).transferComplete(
|
||||||
|
0, 0, 0, _url, "dummy", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getRequest() throws IOException {
|
||||||
|
StringBuilder buf = new StringBuilder(512);
|
||||||
|
URL url = new URL(_actualURL);
|
||||||
|
String proto = url.getProtocol();
|
||||||
|
String host = url.getHost();
|
||||||
|
int port = url.getPort();
|
||||||
|
String path = url.getPath();
|
||||||
|
String query = url.getQuery();
|
||||||
|
if (query != null)
|
||||||
|
path = path + "?" + query;
|
||||||
|
if (!path.startsWith("/"))
|
||||||
|
path = "/" + path;
|
||||||
|
if ( (port == 80) || (port == 443) || (port <= 0) ) path = proto + "://" + host + path;
|
||||||
|
else path = proto + "://" + host + ":" + port + path;
|
||||||
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Requesting " + path);
|
||||||
|
buf.append("HEAD ").append(_actualURL).append(" HTTP/1.1\r\n");
|
||||||
|
buf.append("Host: ").append(url.getHost()).append("\r\n");
|
||||||
|
buf.append("Accept-Encoding: \r\n");
|
||||||
|
if (_shouldProxy)
|
||||||
|
buf.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
|
||||||
|
buf.append("Connection: close\r\n\r\n");
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Request: [" + buf.toString() + "]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We don't decrement the variable (unlike in EepGet), so this is valid */
|
||||||
|
public long getContentLength() {
|
||||||
|
return _bytesRemaining;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user