diff --git a/apps/i2psnark/mime.properties b/apps/i2psnark/mime.properties index 12eea9b1f8f85165e3538804df771b4d47a98d3c..4c0a7a2570df78d4e3b2153d0548afe6d827b00a 100644 --- a/apps/i2psnark/mime.properties +++ b/apps/i2psnark/mime.properties @@ -18,6 +18,7 @@ ogv = video/ogg oga = audio/ogg rar = application/x-rar-compressed su2 = application/zip +su3 = application/zip sud = application/zip tbz = application/x-bzip2 txt = text/plain diff --git a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java index 6f5704ce600e1a16cb53cfa15df4ffc000bb5cd9..17b33a0591c7c60c8c96bb855e86fe1977877a27 100644 --- a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java +++ b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java @@ -135,6 +135,11 @@ public class ConsoleUpdateManager implements UpdateManager { if (ConfigUpdateHandler.USE_SU3_UPDATE) { register(c, ROUTER_SIGNED_SU3, HTTP, 0); register(u, ROUTER_SIGNED_SU3, HTTP, 0); + // todo + //register(c, ROUTER_SIGNED_SU3, HTTPS_CLEARNET, 0); + //register(u, ROUTER_SIGNED_SU3, HTTPS_CLEARNET, -10); + //register(c, ROUTER_SIGNED_SU3, HTTP_CLEARNET, 0); + //register(u, ROUTER_SIGNED_SU3, HTTP_CLEARNET, -20); } // TODO see NewsFetcher //register(u, ROUTER_SIGNED, HTTPS_CLEARNET, -5); diff --git a/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java index e208d024d84a390214d2b3368fc43952cdd11c63..edd3ddbae0ae0a5033a7bfa8f27a2fc9541442ee 100644 --- a/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java @@ -114,6 +114,8 @@ class NewsFetcher extends UpdateRunner { private static final String SU3_KEY = "su3torrent"; private static final String CLEARNET_SUD_KEY = "sudclearnet"; private static final String CLEARNET_SU2_KEY = "su2clearnet"; + private static final String CLEARNET_HTTP_SU3_KEY = "su3clearnet"; + private static final String CLEARNET_HTTPS_SU3_KEY = "su3ssl"; private static final String I2P_SUD_KEY = "sudi2p"; private static final String I2P_SU2_KEY = "su2i2p"; @@ -150,14 +152,9 @@ class NewsFetcher extends UpdateRunner { // Must do su3 first if (ConfigUpdateHandler.USE_SU3_UPDATE) { sourceMap.put(HTTP, _mgr.getUpdateURLs(ROUTER_SIGNED_SU3, "", HTTP)); - String murl = args.get(SU3_KEY); - if (murl != null) { - List<URI> uris = tokenize(murl); - if (!uris.isEmpty()) { - Collections.shuffle(uris, _context.random()); - sourceMap.put(TORRENT, uris); - } - } + addMethod(TORRENT, args.get(SU3_KEY), sourceMap); + addMethod(HTTP_CLEARNET, args.get(CLEARNET_HTTP_SU3_KEY), sourceMap); + addMethod(HTTPS_CLEARNET, args.get(CLEARNET_HTTPS_SU3_KEY), sourceMap); // notify about all sources at once _mgr.notifyVersionAvailable(this, _currentURI, ROUTER_SIGNED_SU3, "", sourceMap, ver, ""); @@ -166,14 +163,7 @@ class NewsFetcher extends UpdateRunner { // now do sud/su2 sourceMap.put(HTTP, _mgr.getUpdateURLs(ROUTER_SIGNED, "", HTTP)); String key = FileUtil.isPack200Supported() ? SU2_KEY : SUD_KEY; - String murl = args.get(key); - if (murl != null) { - List<URI> uris = tokenize(murl); - if (!uris.isEmpty()) { - Collections.shuffle(uris, _context.random()); - sourceMap.put(TORRENT, uris); - } - } + addMethod(TORRENT, args.get(key), sourceMap); // notify about all sources at once _mgr.notifyVersionAvailable(this, _currentURI, ROUTER_SIGNED, "", sourceMap, ver, ""); @@ -280,6 +270,21 @@ class NewsFetcher extends UpdateRunner { return rv; } + /** + * Parse URLs and add to the map + * @param urls may be null + * @since 0.9.9 + */ + private void addMethod(UpdateMethod method, String urls, Map<UpdateMethod, List<URI>> map) { + if (urls != null) { + List<URI> uris = tokenize(urls); + if (!uris.isEmpty()) { + Collections.shuffle(uris, _context.random()); + map.put(method, uris); + } + } + } + /** override to prevent status update */ @Override public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {} diff --git a/apps/routerconsole/java/src/net/i2p/router/update/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/update/UpdateHandler.java index af3f0e39ccd29b3936ce49e53116f9ea06984e4d..949fa372d89598094857e727c8ec41469002dca9 100644 --- a/apps/routerconsole/java/src/net/i2p/router/update/UpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/update/UpdateHandler.java @@ -5,6 +5,8 @@ import java.util.List; import net.i2p.router.RouterContext; import net.i2p.update.*; +import static net.i2p.update.UpdateType.*; +import static net.i2p.update.UpdateMethod.*; /** * <p>Handles the request to update the router by firing one or more @@ -38,10 +40,11 @@ class UpdateHandler implements Updater { */ public UpdateTask update(UpdateType type, UpdateMethod method, List<URI> updateSources, String id, String newVersion, long maxTime) { - if ((type != UpdateType.ROUTER_SIGNED && type != UpdateType.ROUTER_SIGNED_SU3) || - method != UpdateMethod.HTTP || updateSources.isEmpty()) + if ((type != ROUTER_SIGNED && type != ROUTER_SIGNED_SU3) || + ( method != HTTP && method != HTTP_CLEARNET && method != HTTPS_CLEARNET) || + updateSources.isEmpty()) return null; - UpdateRunner update = new UpdateRunner(_context, _mgr, type, updateSources); + UpdateRunner update = new UpdateRunner(_context, _mgr, type, method, updateSources); // set status before thread to ensure UI feedback _mgr.notifyProgress(update, "<b>" + _mgr._("Updating") + "</b>"); return update; diff --git a/apps/routerconsole/java/src/net/i2p/router/update/UpdateRunner.java b/apps/routerconsole/java/src/net/i2p/router/update/UpdateRunner.java index f048752d03fb7459a2ef5a924211fdc666806536..c3b388db2a47fb7552c5990a9f0fc61814bb7519 100644 --- a/apps/routerconsole/java/src/net/i2p/router/update/UpdateRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/update/UpdateRunner.java @@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.net.URI; import java.util.List; +import java.util.Locale; import java.util.StringTokenizer; import net.i2p.crypto.TrustedUpdate; @@ -13,10 +14,12 @@ import net.i2p.router.RouterContext; import net.i2p.router.RouterVersion; import net.i2p.router.web.ConfigUpdateHandler; import net.i2p.update.*; +import static net.i2p.update.UpdateMethod.*; import net.i2p.util.EepGet; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; import net.i2p.util.PartialEepGet; +import net.i2p.util.SSLEepGet; import net.i2p.util.VersionComparator; /** @@ -31,6 +34,7 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList protected final Log _log; protected final ConsoleUpdateManager _mgr; protected final UpdateType _type; + protected final UpdateMethod _method; protected final List<URI> _urls; protected final String _updateFile; protected volatile boolean _isRunning; @@ -58,18 +62,38 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList this(ctx, mgr, type, uris, RouterVersion.VERSION); } + /** + * Uses router version for partial checks + * @since 0.9.9 + */ + public UpdateRunner(RouterContext ctx, ConsoleUpdateManager mgr, UpdateType type, + UpdateMethod method, List<URI> uris) { + this(ctx, mgr, type, method, uris, RouterVersion.VERSION); + } + /** * @param currentVersion used for partial checks * @since 0.9.7 */ public UpdateRunner(RouterContext ctx, ConsoleUpdateManager mgr, UpdateType type, List<URI> uris, String currentVersion) { + this(ctx, mgr, type, HTTP, uris, currentVersion); + } + + /** + * @param method HTTP, HTTP_CLEARNET, or HTTPS_CLEARNET + * @param currentVersion used for partial checks + * @since 0.9.9 + */ + public UpdateRunner(RouterContext ctx, ConsoleUpdateManager mgr, UpdateType type, + UpdateMethod method, List<URI> uris, String currentVersion) { super("Update Runner"); setDaemon(true); _context = ctx; _log = ctx.logManager().getLog(getClass()); _mgr = mgr; _type = type; + _method = method; _urls = uris; _baos = new ByteArrayOutputStream(TrustedUpdate.HEADER_BYTES); _updateFile = (new File(ctx.getTempDir(), "update" + ctx.random().nextInt() + ".tmp")).getAbsolutePath(); @@ -87,7 +111,7 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList public UpdateType getType() { return _type; } - public UpdateMethod getMethod() { return UpdateMethod.HTTP; } + public UpdateMethod getMethod() { return _method; } public URI getURI() { return _currentURI; } @@ -120,9 +144,26 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList // Alternative: In bytesTransferred(), Check the data in the output file after // we've received at least 56 bytes. Need a cancel() method in EepGet ? - 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 = ConfigUpdateHandler.proxyPort(_context); + boolean shouldProxy; + String proxyHost; + int proxyPort; + boolean isSSL = false; + if (_method == HTTP) { + shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue(); + proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); + proxyPort = ConfigUpdateHandler.proxyPort(_context); + } else if (_method == HTTP_CLEARNET) { + shouldProxy = false; + proxyHost = null; + proxyPort = 0; + } else if (_method == HTTPS_CLEARNET) { + shouldProxy = false; + proxyHost = null; + proxyPort = 0; + isSSL = true; + } else { + throw new IllegalArgumentException(); + } if (_urls.isEmpty()) { // not likely, don't bother translating @@ -135,12 +176,23 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList for (URI uri : _urls) { _currentURI = uri; String updateURL = uri.toString(); + if ((_method == HTTP && !"http".equals(uri.getScheme())) || + (_method == HTTP_CLEARNET && !"http".equals(uri.getScheme())) || + (_method == HTTPS_CLEARNET && !"https".equals(uri.getScheme())) || + uri.getHost() == null || + (_method != HTTP && uri.getHost().toLowerCase(Locale.US).endsWith(".i2p"))) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Bad update URI " + uri + " for method " + _method); + continue; + } + updateStatus("<b>" + _("Updating from {0}", linkify(updateURL)) + "</b>"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Selected update URL: " + updateURL); // Check the first 56 bytes for the version - if (shouldProxy) { + // PartialEepGet works with clearnet but not with SSL + if (!isSSL) { _isPartial = true; _baos.reset(); try { @@ -160,6 +212,8 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList if (shouldProxy) // 40 retries!! _get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false); + else if (isSSL) + _get = new SSLEepGet(_context, _updateFile, updateURL); else _get = new EepGet(_context, 1, _updateFile, updateURL, false); _get.addStatusListener(UpdateRunner.this); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java index 85cfd6066ed1cf352b3f457239e68dd9e6e5a1e4..5146d7b0d943ade43c0c55cde55da10068d58680 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java @@ -14,8 +14,6 @@ public class ConfigTunnelsHelper extends HelperBase { private static final String HOPS = ngettext("1 hop", "{0} hops"); private static final String TUNNELS = ngettext("1 tunnel", "{0} tunnels"); - static final String PROP_ADVANCED = "routerconsole.advanced"; - public String getForm() { StringBuilder buf = new StringBuilder(1024); // HTML: <input> cannot be inside a <table> @@ -69,7 +67,7 @@ public class ConfigTunnelsHelper extends HelperBase { private void renderForm(StringBuilder buf, int index, String prefix, String name, TunnelPoolSettings in, TunnelPoolSettings out) { - boolean advanced = _context.getBooleanProperty(PROP_ADVANCED); + boolean advanced = isAdvanced(); buf.append("<tr><th colspan=\"3\"><a name=\"").append(prefix).append("\">"); buf.append(name).append("</a></th></tr>\n"); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java b/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java index 2bee07833bdc12b18c893aa782dd3aa6a2b3c565..ff0dc80592cf53b72661a16bf868901a2d15d8f5 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java @@ -11,6 +11,8 @@ public abstract class HelperBase { protected RouterContext _context; protected Writer _out; + static final String PROP_ADVANCED = "routerconsole.advanced"; + /** * Configure this bean to query a particular router context * @@ -25,6 +27,11 @@ public abstract class HelperBase { } } + /** @since 0.9.9 */ + public boolean isAdvanced() { + return _context.getBooleanProperty(PROP_ADVANCED); + } + /** might be useful in the jsp's */ //public RouterContext getContext() { return _context; } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java index 6be7466a1ebe5d749d85d476ba919e924f396ab1..073ddad1b9ff49258200ca0a900c43e5e6afedca 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java @@ -39,7 +39,7 @@ public class TunnelRenderer { Map<Hash, TunnelPool> clientInboundPools = _context.tunnelManager().getInboundClientPools(); Map<Hash, TunnelPool> clientOutboundPools = _context.tunnelManager().getOutboundClientPools(); destinations = new ArrayList(clientInboundPools.keySet()); - boolean debug = _context.getBooleanProperty(ConfigTunnelsHelper.PROP_ADVANCED); + boolean debug = _context.getBooleanProperty(HelperBase.PROP_ADVANCED); for (int i = 0; i < destinations.size(); i++) { Hash client = destinations.get(i); boolean isLocal = _context.clientManager().isLocal(client); diff --git a/apps/routerconsole/jsp/configupdate.jsp b/apps/routerconsole/jsp/configupdate.jsp index 2d2e54d25782046cf2e580f6e137bc8727a277c4..2fd5e653607aa6e33d09353babd5ca56ac889676 100644 --- a/apps/routerconsole/jsp/configupdate.jsp +++ b/apps/routerconsole/jsp/configupdate.jsp @@ -49,17 +49,21 @@ <td><jsp:getProperty name="updatehelper" property="updatePolicySelectBox" /></td></tr> <% } // if canInstall %> <tr><td class="mediumtags" align="right"><b><%=intl._("Update through the eepProxy?")%></b></td> - <td><jsp:getProperty name="updatehelper" property="updateThroughProxy" /></td> - </tr><tr><td class="mediumtags" align="right"><b><%=intl._("eepProxy host")%>:</b></td> + <td><jsp:getProperty name="updatehelper" property="updateThroughProxy" /></td></tr> + <% if (updatehelper.isAdvanced()) { %> + <tr><td class="mediumtags" align="right"><b><%=intl._("eepProxy host")%>:</b></td> <td><input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /></td> </tr><tr><td class="mediumtags" align="right"><b><%=intl._("eepProxy port")%>:</b></td> <td><input type="text" size="10" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /></td></tr> + <% } // if isAdvanced %> <% if (updatehelper.canInstall()) { %> + <% if (updatehelper.isAdvanced()) { %> <tr><td class="mediumtags" align="right"><b><%=intl._("Update URLs")%>:</b></td> <td><textarea cols="60" rows="6" name="updateURL" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="updateURL" /></textarea></td> </tr><tr><td class="mediumtags" align="right"><b><%=intl._("Trusted keys")%>:</b></td> - <td><textarea cols="60" rows="6" name="trustedKeys" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea></td> - </tr><tr><td id="unsignedbuild" class="mediumtags" align="right"><b><%=intl._("Update with unsigned development builds?")%></b></td> + <td><textarea cols="60" rows="6" name="trustedKeys" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea></td></tr> + <% } // if isAdvanced %> + <tr><td id="unsignedbuild" class="mediumtags" align="right"><b><%=intl._("Update with unsigned development builds?")%></b></td> <td><jsp:getProperty name="updatehelper" property="updateUnsigned" /></td> </tr><tr><td class="mediumtags" align="right"><b><%=intl._("Unsigned Build URL")%>:</b></td> <td><input type="text" size="60" name="zipURL" value="<jsp:getProperty name="updatehelper" property="zipURL" />"></td></tr> diff --git a/core/java/src/net/i2p/util/PartialEepGet.java b/core/java/src/net/i2p/util/PartialEepGet.java index d557137307865ad9c33d5fbd367189bb93c4ed79..7fe625b66c2e9b9c0ee221031d4a90f388186b88 100644 --- a/core/java/src/net/i2p/util/PartialEepGet.java +++ b/core/java/src/net/i2p/util/PartialEepGet.java @@ -11,6 +11,9 @@ import net.i2p.I2PAppContext; * Fetch exactly the first 'size' bytes into a stream * Anything less or more will throw an IOException * No retries, no min and max size options, no timeout option + * If the server does not return a Content-Length header of the correct size, + * the fetch will fail. + * * Useful for checking .sud versions * * @since 0.7.12 @@ -19,7 +22,13 @@ import net.i2p.I2PAppContext; public class PartialEepGet extends EepGet { long _fetchSize; - /** @param size fetch exactly this many bytes */ + /** + * Instantiate an EepGet that will fetch exactly size bytes when fetch() is called. + * + * @param proxyHost use null or "" for no proxy + * @param proxyPort use 0 for no proxy + * @param size fetch exactly this many bytes + */ public PartialEepGet(I2PAppContext ctx, String proxyHost, int proxyPort, OutputStream outputStream, String url, long size) { // we're using this constructor: @@ -88,7 +97,8 @@ public class PartialEepGet extends EepGet { } private static void usage() { - System.err.println("PartialEepGet [-p 127.0.0.1:4444] [-l #bytes] url"); + System.err.println("PartialEepGet [-p 127.0.0.1:4444] [-l #bytes] url\n" + + " (use -p :0 for no proxy)"); } @Override diff --git a/core/java/src/net/i2p/util/SSLEepGet.java b/core/java/src/net/i2p/util/SSLEepGet.java index e7a060230fc78e3a09c6ef763c4f87f0af03850e..3866f3d1ab42124a16326cd13128155d264f3137 100644 --- a/core/java/src/net/i2p/util/SSLEepGet.java +++ b/core/java/src/net/i2p/util/SSLEepGet.java @@ -103,9 +103,39 @@ public class SSLEepGet extends EepGet { * @since 0.8.2 */ public SSLEepGet(I2PAppContext ctx, OutputStream outputStream, String url, SSLState state) { + this(ctx, null, outputStream, url, null); + } + + /** + * A new SSLEepGet with a new SSLState + * @since 0.9.9 + */ + public SSLEepGet(I2PAppContext ctx, String outputFile, String url) { + this(ctx, outputFile, url, null); + } + + /** + * @param state an SSLState retrieved from a previous SSLEepGet with getSSLState(), or null. + * This makes repeated fetches from the same host MUCH faster, + * and prevents repeated key store loads even for different hosts. + * @since 0.9.9 + */ + public SSLEepGet(I2PAppContext ctx, String outputFile, String url, SSLState state) { + this(ctx, outputFile, null, url, null); + } + + /** + * outputFile, outputStream: One null, one non-null + * + * @param state an SSLState retrieved from a previous SSLEepGet with getSSLState(), or null. + * This makes repeated fetches from the same host MUCH faster, + * and prevents repeated key store loads even for different hosts. + * @since 0.9.9 + */ + private SSLEepGet(I2PAppContext ctx, String outputFile, OutputStream outputStream, String url, SSLState state) { // 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, false, null, -1, 0, -1, -1, null, outputStream, url, true, null, null); + super(ctx, false, null, -1, 0, -1, -1, outputFile, outputStream, url, true, null, null); if (state != null && state.context != null) _sslContext = state.context; else