diff --git a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java index 34b550df67140765d9ea7ab0fd1580f1b592e1ac..077acdb7f02bbf7f1a8c13cd912cac599d994078 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java @@ -39,6 +39,7 @@ import net.i2p.I2PAppContext; public class Daemon { public static final String VERSION = "2.0.3"; private static final Daemon _instance = new Daemon(); + private boolean _running; /** * Update the router and published address books using remote data from the @@ -126,6 +127,7 @@ public class Daemon { } public void run(String[] args) { + _running = true; String settingsLocation = "config.txt"; File homeFile; if (args.length > 0) { @@ -166,7 +168,7 @@ public class Daemon { // Static method, and redundent Thread.currentThread().sleep(5*60*1000); } catch (InterruptedException ie) {} - while (true) { + while (_running) { long delay = Long.parseLong((String) settings.get("update_delay")); if (delay < 1) { delay = 1; @@ -179,6 +181,8 @@ public class Daemon { } } catch (InterruptedException exp) { } + if (!_running) + break; settings = ConfigParser.parse(settingsFile, defaultSettings); } } @@ -192,4 +196,9 @@ public class Daemon { _instance.notifyAll(); } } + + public static void stop() { + _instance._running = false; + wakeup(); + } } diff --git a/apps/addressbook/java/src/net/i2p/addressbook/DaemonThread.java b/apps/addressbook/java/src/net/i2p/addressbook/DaemonThread.java index b5afaaa2f5d6b680032e2c5c118ef0f73f4dbb9a..7c9e65994fc513571253e6d947b35aa7c264c434 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/DaemonThread.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/DaemonThread.java @@ -51,4 +51,9 @@ public class DaemonThread extends Thread { //} Daemon.main(this.args); } -} \ No newline at end of file + + public void halt() { + Daemon.stop(); + interrupt(); + } +} diff --git a/apps/addressbook/java/src/net/i2p/addressbook/Servlet.java b/apps/addressbook/java/src/net/i2p/addressbook/Servlet.java index fceecfe5397be64ca01ec05470c1548c1d9de9ce..beb225ca0d6d60fcea8b69585b24abcbf91240d8 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/Servlet.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/Servlet.java @@ -41,7 +41,7 @@ import javax.servlet.http.HttpServletResponse; * */ public class Servlet extends HttpServlet { - private Thread thread; + private DaemonThread thread; private String nonce; private static final String PROP_NONCE = "addressbook.nonce"; @@ -88,4 +88,9 @@ public class Servlet extends HttpServlet { //System.out.println("INFO: config root under " + args[0]); } + @Override + public void destroy() { + this.thread.halt(); + super.destroy(); + } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index c909a112f011aa4de23efb4014c80eba3b89803a..d0d9a3bd8476d59c95c291ca3ddcc66016ab7683 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -40,6 +40,8 @@ public class SnarkManager implements Snark.CompleteListener { private I2PSnarkUtil _util; private PeerCoordinatorSet _peerCoordinatorSet; private ConnectionAcceptor _connectionAcceptor; + private Thread _monitor; + private boolean _running; public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost"; public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort"; @@ -78,15 +80,22 @@ public class SnarkManager implements Snark.CompleteListener { * for i2cp host/port or i2psnark.dir */ public void start() { + _running = true; _peerCoordinatorSet = new PeerCoordinatorSet(); _connectionAcceptor = new ConnectionAcceptor(_util); int minutes = getStartupDelayMinutes(); _messages.add(_("Adding torrents in {0} minutes", minutes)); - I2PAppThread monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor"); - monitor.setDaemon(true); - monitor.start(); + _monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true); + _monitor.start(); _context.addShutdownTask(new SnarkManagerShutdown()); } + + public void stop() { + _running = false; + _monitor.interrupt(); + _connectionAcceptor.halt(); + (new SnarkManagerShutdown()).run(); + } /** hook to I2PSnarkUtil for the servlet */ public I2PSnarkUtil util() { return _util; } diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index c6f22a63df43230abb7f3bbc6496405067911aa2..cb72f2c1e06936f2472b9b17a5499a992aa5ea80 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -150,7 +150,7 @@ public class TrackerClient extends I2PAppThread continue; String dest = _util.lookup(url.substring(7, slash)); if (dest == null) { - _log.error("Announce host unknown: [" + url + "]"); + _log.error("Announce host unknown: [" + url.substring(7, slash) + "]"); continue; } if (primary.startsWith("http://" + dest)) @@ -258,7 +258,7 @@ public class TrackerClient extends I2PAppThread tr.started = true; Set peers = info.getPeers(); - tr.seenPeers = peers.size(); + tr.seenPeers = info.getPeerCount(); if (coordinator.trackerSeenPeers < tr.seenPeers) // update rising number quickly coordinator.trackerSeenPeers = tr.seenPeers; if ( (left > 0) && (!completed) ) { @@ -269,6 +269,7 @@ public class TrackerClient extends I2PAppThread Iterator it = ordered.iterator(); while (it.hasNext()) { Peer cur = (Peer)it.next(); + // FIXME if id == us || dest == us continue; // only delay if we actually make an attempt to add peer if(coordinator.addPeer(cur)) { int delay = DELAY_MUL; @@ -356,6 +357,10 @@ public class TrackerClient extends I2PAppThread + "&downloaded=" + downloaded + "&left=" + left + ((! event.equals(NO_EVENT)) ? ("&event=" + event) : ""); + if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers()) + s += "&numwant=0"; + else + s += "&numwant=" + _util.getMaxConnections(); _util.debug("Sending TrackerClient request: " + s, Snark.INFO); tr.lastRequestTime = System.currentTimeMillis(); @@ -430,7 +435,7 @@ public class TrackerClient extends I2PAppThread url.getPort() < 0; } - private class Tracker + private static class Tracker { String announce; boolean isPrimary; diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java index c2a287270d290d8afc11257b897b2a7bbedad95b..84198f12f0f0eacca955c53cf14a018c91202f1e 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java @@ -23,6 +23,7 @@ package org.klomp.snark; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -37,6 +38,8 @@ public class TrackerInfo private final String failure_reason; private final int interval; private final Set peers; + private int complete; + private int incomplete; public TrackerInfo(InputStream in, byte[] my_id, MetaInfo metainfo) throws IOException @@ -68,11 +71,26 @@ public class TrackerInfo throw new InvalidBEncodingException("No interval given"); else interval = beInterval.getInt(); + BEValue bePeers = (BEValue)m.get("peers"); if (bePeers == null) - throw new InvalidBEncodingException("No peer list"); + peers = Collections.EMPTY_SET; else peers = getPeers(bePeers.getList(), my_id, metainfo); + + BEValue bev = (BEValue)m.get("complete"); + if (bev != null) try { + complete = bev.getInt(); + if (complete < 0) + complete = 0; + } catch (InvalidBEncodingException ibe) {} + + bev = (BEValue)m.get("incomplete"); + if (bev != null) try { + incomplete = bev.getInt(); + if (incomplete < 0) + incomplete = 0; + } catch (InvalidBEncodingException ibe) {} } } @@ -115,6 +133,12 @@ public class TrackerInfo return peers; } + public int getPeerCount() + { + int pc = peers == null ? 0 : peers.size(); + return Math.max(pc, complete + incomplete - 1); + } + public String getFailureReason() { return failure_reason; @@ -132,6 +156,8 @@ public class TrackerInfo return "TrackerInfo[FAILED: " + failure_reason + "]"; else return "TrackerInfo[interval=" + interval + + (complete > 0 ? (", complete=" + complete) : "" ) + + (incomplete > 0 ? (", incomplete=" + incomplete) : "" ) + ", peers=" + peers + "]"; } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 013d70a94c3b2e87fc6104415557203f043e87ba..2a113051315d87de105b43b91dae212884fc6a40 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -59,6 +59,12 @@ public class I2PSnarkServlet extends HttpServlet { _manager.start(); } + @Override + public void destroy() { + _manager.stop(); + super.destroy(); + } + @Override public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java index de71ff3b2fdd43336b2bf70e3dbc6007700c3fa2..af4049a21b09183a70f2e84f4d588873e937535a 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java @@ -124,13 +124,13 @@ class HTTPResponseOutputStream extends FilterOutputStream { * Tweak that first HTTP response line (HTTP 200 OK, etc) * */ - protected String filterResponseLine(String line) { + protected static String filterResponseLine(String line) { return line; } /** we ignore any potential \r, since we trim it on write anyway */ private static final byte NL = '\n'; - private boolean isNL(byte b) { return (b == NL); } + private static boolean isNL(byte b) { return (b == NL); } /** ok, received, now munge & write it */ private void writeHeader() throws IOException { @@ -275,7 +275,8 @@ class HTTPResponseOutputStream extends FilterOutputStream { _context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start); } } - private class InternalGZIPInputStream extends GZIPInputStream { + + private static class InternalGZIPInputStream extends GZIPInputStream { public InternalGZIPInputStream(InputStream in) throws IOException { super(in); } @@ -318,6 +319,7 @@ class HTTPResponseOutputStream extends FilterOutputStream { return super.toString() + ": " + _in; } +/******* public static void main(String args[]) { String simple = "HTTP/1.1 200 OK\n" + "foo: bar\n" + @@ -367,7 +369,6 @@ class HTTPResponseOutputStream extends FilterOutputStream { "A:\n" + "\n"; - /* */ test("Simple", simple, true); test("Filtered", filtered, true); test("Filtered windows", winfilter, true); @@ -382,7 +383,6 @@ class HTTPResponseOutputStream extends FilterOutputStream { test("Invalid (bad headers)", invalid5, true); test("Invalid (bad headers2)", invalid6, false); test("Invalid (bad headers3)", invalid7, false); - /* */ } private static void test(String name, String orig, boolean shouldPass) { @@ -401,4 +401,5 @@ class HTTPResponseOutputStream extends FilterOutputStream { System.out.println("Properly fails with " + e.getMessage()); } } +******/ } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java index a00fc7d8de98f29bb01ab0a86accf40289c9b955..26190dda78172d12bb81b303ff448b16f8c26855 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -213,7 +213,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { sockMgr.getSession().destroySession(); } catch (I2PException ex) { _log.error("Error destroying the session", ex); - System.exit(1); + //System.exit(1); } l.log("Server shut down."); open = false; diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java index d6c98c52295f37150222a98c90024f3d351dc11f..91ac9726f055f0f427051d6e980a73fa05f28164 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java @@ -11,7 +11,6 @@ import java.util.Set; import net.i2p.router.startup.ClientAppConfig; import net.i2p.router.startup.LoadClientAppsJob; -import net.i2p.util.Log; import org.mortbay.jetty.Server; @@ -19,15 +18,19 @@ import org.mortbay.jetty.Server; * Saves changes to clients.config or webapps.config */ public class ConfigClientsHandler extends FormHandler { - private Log configClient_log; private Map _settings; - public ConfigClientsHandler() { - configClient_log = ContextHelper.getContext(null).logManager().getLog(ConfigClientsHandler.class); - } - @Override protected void processForm() { + // set action for when CR is hit in a text input box + if (_action.length() <= 0) { + String url = getJettyString("pluginURL"); + if (url != null && url.length() > 0) + _action = "Install Plugin"; + else + _action = "Save Client Configuration"; + } + if (_action.equals(_("Save Client Configuration"))) { saveClientChanges(); return; @@ -200,7 +203,7 @@ public class ConfigClientsHandler extends FormHandler { return; } ClientAppConfig ca = clients.get(i); - LoadClientAppsJob.runClient(ca.className, ca.clientName, LoadClientAppsJob.parseArgs(ca.args), configClient_log); + LoadClientAppsJob.runClient(ca.className, ca.clientName, LoadClientAppsJob.parseArgs(ca.args), _log); addFormNotice(_("Client") + ' ' + _(ca.clientName) + ' ' + _("started") + '.'); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java index a897f84e407f2b604e795672b2e15154aea4629b..9dbf4de23a6552a1c69bfb73533f6850fbf428ca 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java @@ -43,11 +43,11 @@ public class ConfigClientsHelper extends HelperBase { renderForm(buf, ""+cur, ca.clientName, false, !ca.disabled, "webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName), ca.className + ((ca.args != null) ? " " + ca.args : ""), (""+cur).equals(_edit), - true, false, false, true); + true, false, false, true, !ca.disabled); } if ("new".equals(_edit)) - renderForm(buf, "" + clients.size(), "", false, false, false, "", true, false, false, false, false); + renderForm(buf, "" + clients.size(), "", false, false, false, "", true, false, false, false, false, false); buf.append("</table>\n"); return buf.toString(); } @@ -65,7 +65,7 @@ public class ConfigClientsHelper extends HelperBase { String val = props.getProperty(name); renderForm(buf, app, app, !"addressbook".equals(app), "true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war", - false, false, false, false, false); + false, false, false, false, false, true); } } buf.append("</table>\n"); @@ -149,7 +149,7 @@ public class ConfigClientsHelper extends HelperBase { boolean enableStop = !Boolean.valueOf(appProps.getProperty("disableStop")).booleanValue(); renderForm(buf, app, app, false, "true".equals(val), false, desc.toString(), false, false, - updateURL != null, enableStop, true); + updateURL != null, enableStop, true, true); } } buf.append("</table>\n"); @@ -160,7 +160,7 @@ public class ConfigClientsHelper extends HelperBase { private void renderForm(StringBuilder buf, String index, String name, boolean urlify, boolean enabled, boolean ro, String desc, boolean edit, boolean showEditButton, boolean showUpdateButton, boolean showStopButton, - boolean showDeleteButton) { + boolean showDeleteButton, boolean showStartButton) { buf.append("<tr><td class=\"mediumtags\" align=\"right\" width=\"25%\">"); if (urlify && enabled) { String link = "/"; @@ -183,7 +183,7 @@ public class ConfigClientsHelper extends HelperBase { buf.append("disabled=\"true\" "); } buf.append("></td><td align=\"center\" width=\"15%\">"); - if ((!enabled) && !edit) { + if (showStartButton && (!ro) && !edit) { buf.append("<button type=\"submit\" name=\"action\" value=\"Start ").append(index).append("\" >" + _("Start") + "<span class=hide> ").append(index).append("</span></button>"); } if (showEditButton && (!edit) && !ro) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NavHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/NavHelper.java index df7dc6697ee817134b96b762709ec359aacb3d19..844a76cb203b6b532f4ab2bfdf1827894891d030 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NavHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NavHelper.java @@ -1,6 +1,8 @@ package net.i2p.router.web; -import java.util.Iterator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -31,9 +33,12 @@ public class NavHelper { if (_apps.isEmpty()) return ""; StringBuilder buf = new StringBuilder(256); - for (Iterator<String> iter = _apps.keySet().iterator(); iter.hasNext(); ) { - String name = iter.next(); + List<String> l = new ArrayList(_apps.keySet()); + Collections.sort(l); + for (String name : l) { String path = _apps.get(name); + if (path == null) + continue; buf.append(" <a target=\"_top\" href=\"").append(path).append("\">"); buf.append(name).append("</a>"); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java index 3497bc86751e0f7a115ab442ad88a325e2f73434..effcca6bf02b0ca4c64e6a7e4f9d1d480e38f886 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java @@ -286,11 +286,11 @@ public class NetDbRenderer { buf.append("<b>").append(DataHelper.stripHTML(style)).append(":</b> "); int cost = addr.getCost(); if (!((style.equals("SSU") && cost == 5) || (style.equals("NTCP") && cost == 10))) - buf.append('[').append("cost").append('=').append("" + cost).append("] "); + buf.append('[').append(_("cost")).append('=').append("" + cost).append("] "); for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) { String name = (String)optIter.next(); String val = addr.getOptions().getProperty(name); - buf.append('[').append(DataHelper.stripHTML(name)).append('=').append(DataHelper.stripHTML(val)).append("] "); + buf.append('[').append(_(DataHelper.stripHTML(name))).append('=').append(DataHelper.stripHTML(val)).append("] "); } } buf.append("</td></tr>\n"); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java index 1e14b8917d7c28487e4cbe25a39a0eddd268a3d2..cdeaaeb9350a05efd22978f7a924cfd26edaaeb6 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -35,7 +35,7 @@ import org.mortbay.jetty.Server; * @author zzz */ public class PluginStarter implements Runnable { - private RouterContext _context; + protected RouterContext _context; static final String PREFIX = "plugin."; static final String ENABLED = ".startOnLoad"; private static final String[] STANDARD_WEBAPPS = { "i2psnark", "i2ptunnel", "susidns", @@ -223,7 +223,8 @@ public class PluginStarter implements Runnable { if (name != null && name.length() > 0) NavHelper.unregisterApp(name); - log.error("Stopping plugin: " + appName); + if (log.shouldLog(Log.WARN)) + log.warn("Stopping plugin: " + appName); return true; } @@ -426,7 +427,8 @@ public class PluginStarter implements Runnable { } try { addPath(f.toURI().toURL()); - log.error("INFO: Adding plugin to classpath: " + f); + if (log.shouldLog(Log.WARN)) + log.warn("INFO: Adding plugin to classpath: " + f); } catch (Exception e) { log.error("Plugin client " + clientName + " bad classpath element: " + f, e); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStopper.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStopper.java new file mode 100644 index 0000000000000000000000000000000000000000..2f29eddf273e66f67b0266fc7ae8d7e360667292 --- /dev/null +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStopper.java @@ -0,0 +1,40 @@ +package net.i2p.router.web; + +import net.i2p.router.RouterContext; +import net.i2p.util.Log; + +/** + * Stop all plugins that are installed + * + * @since 0.7.13 + * @author zzz + */ +public class PluginStopper extends PluginStarter { + + public PluginStopper(RouterContext ctx) { + super(ctx); + } + + @Override + public void run() { + stopPlugins(_context); + } + + /** + * Stop all plugins + * (whether or not they were ever started) + * + * this shouldn't throw anything + */ + static void stopPlugins(RouterContext ctx) { + Log log = ctx.logManager().getLog(PluginStopper.class); + for (String app : getPlugins()) { + try { + stopPlugin(ctx, app); + } catch (Throwable e) { + if (log.shouldLog(Log.WARN)) + log.warn("Failed to stop plugin: " + app, e); + } + } + } +} diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java index 8645df585559f8ef83581766cceb1401fcece6cc..9ffc903f06eab51891ede050d0e640f2e6dbdd62 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java @@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.util.List; import java.util.Properties; import net.i2p.I2PAppContext; @@ -14,6 +15,8 @@ import net.i2p.util.EepGet; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; import net.i2p.util.PartialEepGet; +import net.i2p.util.SimpleScheduler; +import net.i2p.util.SimpleTimer; import net.i2p.util.VersionComparator; /** @@ -46,6 +49,20 @@ public class PluginUpdateChecker extends UpdateHandler { super(ctx); } + /** check all plugins */ + public void update() { + Thread t = new I2PAppThread(new AllCheckerRunner(), "AllAppChecker", true); + t.start(); + } + + public class AllCheckerRunner implements Runnable { + public void run() { + List<String> plugins = PluginStarter.getPlugins(); + // TODO + } + } + + /** check a single plugin */ public void update(String appName) { // don't block waiting for the other one to finish if ("true".equals(System.getProperty(PROP_UPDATE_IN_PROGRESS))) { @@ -84,6 +101,21 @@ public class PluginUpdateChecker extends UpdateHandler { return false; } + private void scheduleStatusClean(String msg) { + SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000); + } + + private class Cleaner implements SimpleTimer.TimedEvent { + private String _msg; + public Cleaner(String msg) { + _msg = msg; + } + public void timeReached() { + if (_msg.equals(getStatus())) + updateStatus(""); + } + } + public class PluginUpdateCheckerRunner extends UpdateRunner implements Runnable, EepGet.StatusListener { ByteArrayOutputStream _baos; @@ -116,17 +148,22 @@ public class PluginUpdateChecker extends UpdateHandler { public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray())); boolean newer = (new VersionComparator()).compare(newVersion, _oldVersion) > 0; + String msg; if (newer) - updateStatus("<b>" + _("New plugin version {0} is available", newVersion) + "</b>"); + msg = "<b>" + _("New plugin version {0} is available", newVersion) + "</b>"; else - updateStatus("<b>" + _("No new version is available for plugin {0}", _appName) + "</b>"); + msg = "<b>" + _("No new version is available for plugin {0}", _appName) + "</b>"; + updateStatus(msg); + scheduleStatusClean(msg); } @Override public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) { File f = new File(_updateFile); f.delete(); - updateStatus("<b>" + _("Update check failed for plugin {0}", _appName) + "</b>"); + String msg = "<b>" + _("Update check failed for plugin {0}", _appName) + "</b>"; + updateStatus(msg); + scheduleStatusClean(msg); } } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java index f8e0f3aa4068eab700c05850eea5255a50f78c5b..e028ee3cdc53f010e722f8465c8c5ef0ed53cfc6 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java @@ -16,6 +16,8 @@ import net.i2p.util.FileUtil; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; import net.i2p.util.OrderedProperties; +import net.i2p.util.SimpleScheduler; +import net.i2p.util.SimpleTimer; import net.i2p.util.VersionComparator; /** @@ -89,6 +91,21 @@ public class PluginUpdateHandler extends UpdateHandler { return false; } + private void scheduleStatusClean(String msg) { + SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000); + } + + private class Cleaner implements SimpleTimer.TimedEvent { + private String _msg; + public Cleaner(String msg) { + _msg = msg; + } + public void timeReached() { + if (_msg.equals(getStatus())) + updateStatus(""); + } + } + public class PluginUpdateRunner extends UpdateRunner implements Runnable, EepGet.StatusListener { public PluginUpdateRunner(String url) { @@ -136,7 +153,7 @@ public class PluginUpdateHandler extends UpdateHandler { File appDir = new File(_context.getAppDir(), PLUGIN_DIR); if ((!appDir.exists()) && (!appDir.mkdir())) { f.delete(); - updateStatus("<b>" + _("Cannot create plugin directory {0}", appDir.getAbsolutePath()) + "</b>"); + statusDone("<b>" + _("Cannot create plugin directory {0}", appDir.getAbsolutePath()) + "</b>"); return; } @@ -145,7 +162,7 @@ public class PluginUpdateHandler extends UpdateHandler { // extract to a zip file whether the sig is good or not, so we can get the properties file String err = up.migrateFile(f, to); if (err != null) { - updateStatus("<b>" + err + ' ' + _("from {0}", url) + " </b>"); + statusDone("<b>" + err + ' ' + _("from {0}", url) + " </b>"); f.delete(); to.delete(); return; @@ -155,7 +172,7 @@ public class PluginUpdateHandler extends UpdateHandler { f.delete(); to.delete(); FileUtil.rmdir(tempDir, false); - updateStatus("<b>" + _("Plugin from {0} is corrupt", url) + "</b>"); + statusDone("<b>" + _("Plugin from {0} is corrupt", url) + "</b>"); return; } File installProps = new File(tempDir, "plugin.config"); @@ -166,7 +183,7 @@ public class PluginUpdateHandler extends UpdateHandler { f.delete(); to.delete(); FileUtil.rmdir(tempDir, false); - updateStatus("<b>" + _("Plugin from {0} does not contain the required configuration file", url) + "</b>"); + statusDone("<b>" + _("Plugin from {0} does not contain the required configuration file", url) + "</b>"); return; } // we don't need this anymore, we will unzip again @@ -179,7 +196,7 @@ public class PluginUpdateHandler extends UpdateHandler { f.delete(); to.delete(); //updateStatus("<b>" + "Plugin contains an invalid key" + ' ' + pubkey + ' ' + signer + "</b>"); - updateStatus("<b>" + _("Plugin from {0} contains an invalid key", url) + "</b>"); + statusDone("<b>" + _("Plugin from {0} contains an invalid key", url) + "</b>"); return; } @@ -198,7 +215,7 @@ public class PluginUpdateHandler extends UpdateHandler { if (!signer.equals(signingKeyName)) { f.delete(); to.delete(); - updateStatus("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>"); + statusDone("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>"); return; } } else { @@ -207,7 +224,7 @@ public class PluginUpdateHandler extends UpdateHandler { // bad or duplicate key f.delete(); to.delete(); - updateStatus("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>"); + statusDone("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>"); return; } // ...and try the verify again @@ -216,7 +233,7 @@ public class PluginUpdateHandler extends UpdateHandler { if (!signer.equals(signingKeyName)) { f.delete(); to.delete(); - updateStatus("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>"); + statusDone("<b>" + _("Plugin signature verification of {0} failed", url) + "</b>"); return; } } @@ -231,12 +248,12 @@ public class PluginUpdateHandler extends UpdateHandler { version.indexOf("<") >= 0 || version.indexOf(">") >= 0 || appName.startsWith(".") || appName.indexOf("/") >= 0 || appName.indexOf("\\") >= 0) { to.delete(); - updateStatus("<b>" + _("Plugin from {0} has invalid name or version", url) + "</b>"); + statusDone("<b>" + _("Plugin from {0} has invalid name or version", url) + "</b>"); return; } if (!version.equals(sudVersion)) { to.delete(); - updateStatus("<b>" + _("Plugin {0} has mismatched versions", appName) + "</b>"); + statusDone("<b>" + _("Plugin {0} has mismatched versions", appName) + "</b>"); return; } @@ -244,7 +261,7 @@ public class PluginUpdateHandler extends UpdateHandler { if (minVersion != null && (new VersionComparator()).compare(CoreVersion.VERSION, minVersion) < 0) { to.delete(); - updateStatus("<b>" + _("This plugin requires I2P version {0} or higher", minVersion) + "</b>"); + statusDone("<b>" + _("This plugin requires I2P version {0} or higher", minVersion) + "</b>"); return; } @@ -252,7 +269,7 @@ public class PluginUpdateHandler extends UpdateHandler { if (minVersion != null && (new VersionComparator()).compare(System.getProperty("java.version"), minVersion) < 0) { to.delete(); - updateStatus("<b>" + _("This plugin requires Java version {0} or higher", minVersion) + "</b>"); + statusDone("<b>" + _("This plugin requires Java version {0} or higher", minVersion) + "</b>"); return; } @@ -260,7 +277,7 @@ public class PluginUpdateHandler extends UpdateHandler { if (destDir.exists()) { if (Boolean.valueOf(props.getProperty("install-only")).booleanValue()) { to.delete(); - updateStatus("<b>" + _("Downloaded plugin is for new installs only, but the plugin is already installed", url) + "</b>"); + statusDone("<b>" + _("Downloaded plugin is for new installs only, but the plugin is already installed", url) + "</b>"); return; } @@ -272,7 +289,7 @@ public class PluginUpdateHandler extends UpdateHandler { } catch (IOException ioe) { to.delete(); FileUtil.rmdir(tempDir, false); - updateStatus("<b>" + _("Installed plugin does not contain the required configuration file", url) + "</b>"); + statusDone("<b>" + _("Installed plugin does not contain the required configuration file", url) + "</b>"); return; } String oldPubkey = oldProps.getProperty("key"); @@ -280,28 +297,28 @@ public class PluginUpdateHandler extends UpdateHandler { String oldAppName = props.getProperty("name"); if ((!pubkey.equals(oldPubkey)) || (!signer.equals(oldKeyName)) || (!appName.equals(oldAppName))) { to.delete(); - updateStatus("<b>" + _("Signature of downloaded plugin does not match installed plugin") + "</b>"); + statusDone("<b>" + _("Signature of downloaded plugin does not match installed plugin") + "</b>"); return; } String oldVersion = oldProps.getProperty("version"); if (oldVersion == null || (new VersionComparator()).compare(oldVersion, version) >= 0) { to.delete(); - updateStatus("<b>" + _("Downloaded plugin version {0} is not newer than installed plugin", version) + "</b>"); + statusDone("<b>" + _("Downloaded plugin version {0} is not newer than installed plugin", version) + "</b>"); return; } minVersion = ConfigClientsHelper.stripHTML(props, "min-installed-version"); if (minVersion != null && (new VersionComparator()).compare(minVersion, oldVersion) > 0) { to.delete(); - updateStatus("<b>" + _("Plugin update requires installed plugin version {0} or higher", minVersion) + "</b>"); + statusDone("<b>" + _("Plugin update requires installed plugin version {0} or higher", minVersion) + "</b>"); return; } String maxVersion = ConfigClientsHelper.stripHTML(props, "max-installed-version"); if (maxVersion != null && (new VersionComparator()).compare(maxVersion, oldVersion) < 0) { to.delete(); - updateStatus("<b>" + _("Plugin update requires installed plugin version {0} or lower", maxVersion) + "</b>"); + statusDone("<b>" + _("Plugin update requires installed plugin version {0} or lower", maxVersion) + "</b>"); return; } @@ -318,12 +335,12 @@ public class PluginUpdateHandler extends UpdateHandler { } else { if (Boolean.valueOf(props.getProperty("update-only")).booleanValue()) { to.delete(); - updateStatus("<b>" + _("Plugin is for upgrades only, but the plugin is not installed") + "</b>"); + statusDone("<b>" + _("Plugin is for upgrades only, but the plugin is not installed") + "</b>"); return; } if (!destDir.mkdir()) { to.delete(); - updateStatus("<b>" + _("Cannot create plugin directory {0}", destDir.getAbsolutePath()) + "</b>"); + statusDone("<b>" + _("Cannot create plugin directory {0}", destDir.getAbsolutePath()) + "</b>"); return; } } @@ -331,16 +348,16 @@ public class PluginUpdateHandler extends UpdateHandler { // Finally, extract the zip to the plugin directory if (!FileUtil.extractZip(to, destDir)) { to.delete(); - updateStatus("<b>" + _("Failed to install plugin in {0}", destDir.getAbsolutePath()) + "</b>"); + statusDone("<b>" + _("Failed to install plugin in {0}", destDir.getAbsolutePath()) + "</b>"); return; } to.delete(); if (Boolean.valueOf(props.getProperty("dont-start-at-install")).booleanValue()) { if (Boolean.valueOf(props.getProperty("router-restart-required")).booleanValue()) - updateStatus("<b>" + _("Plugin {0} installed, router restart required", appName) + "</b>"); + statusDone("<b>" + _("Plugin {0} installed, router restart required", appName) + "</b>"); else { - updateStatus("<b>" + _("Plugin {0} installed", appName) + "</b>"); + statusDone("<b>" + _("Plugin {0} installed", appName) + "</b>"); Properties pluginProps = PluginStarter.pluginProperties(); pluginProps.setProperty(PluginStarter.PREFIX + appName + PluginStarter.ENABLED, "false"); PluginStarter.storePluginProperties(pluginProps); @@ -349,11 +366,11 @@ public class PluginUpdateHandler extends UpdateHandler { // start everything try { if (PluginStarter.startPlugin(_context, appName)) - updateStatus("<b>" + _("Plugin {0} installed and started", appName) + "</b>"); + statusDone("<b>" + _("Plugin {0} installed and started", appName) + "</b>"); else - updateStatus("<b>" + _("Plugin {0} installed but failed to start, check logs", appName) + "</b>"); + statusDone("<b>" + _("Plugin {0} installed but failed to start, check logs", appName) + "</b>"); } catch (Throwable e) { - updateStatus("<b>" + _("Plugin {0} installed but failed to start", appName) + ": " + e + "</b>"); + statusDone("<b>" + _("Plugin {0} installed but failed to start", appName) + ": " + e + "</b>"); _log.error("Error starting plugin " + appName, e); } } @@ -363,8 +380,14 @@ public class PluginUpdateHandler extends UpdateHandler { public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) { File f = new File(_updateFile); f.delete(); - updateStatus("<b>" + _("Failed to download plugin from {0}", url) + "</b>"); + statusDone("<b>" + _("Failed to download plugin from {0}", url) + "</b>"); + } + + private void statusDone(String msg) { + updateStatus(msg); + scheduleStatusClean(msg); } + } @Override diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java index ef5d2846c238a7260b74972a43e449e03c9868c9..460db8d1765679bd4c2684d85fefdfc9b660c418 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -190,9 +190,11 @@ public class RouterConsoleRunner { List<RouterContext> contexts = RouterContext.listContexts(); if (contexts != null) { - if (PluginStarter.pluginsEnabled(contexts.get(0))) { - t = new I2PAppThread(new PluginStarter(contexts.get(0)), "PluginStarter", true); + RouterContext ctx = contexts.get(0); + if (PluginStarter.pluginsEnabled(ctx)) { + t = new I2PAppThread(new PluginStarter(ctx), "PluginStarter", true); t.start(); + ctx.addShutdownTask(new PluginStopper(ctx)); } } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/WebAppStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/WebAppStarter.java index 048ef31cc67619bcb43e0c92f470df9207c4ec94..8a146f5b1ba05b1683f423b322e59893f4afcd85 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/WebAppStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/WebAppStarter.java @@ -43,9 +43,17 @@ public class WebAppStarter { /** * add but don't start + * This is used only by RouterConsoleRunner, which adds all the webapps first + * and then starts all at once. */ static WebApplicationContext addWebApp(I2PAppContext ctx, Server server, String appName, String warPath, File tmpdir) throws IOException { + // Jetty will happily load one context on top of another without stopping + // the first one, so we remove any previous one here + try { + stopWebApp(server, appName); + } catch (Throwable t) {} + WebApplicationContext wac = server.addWebApplication("/"+ appName, warPath); tmpdir.mkdir(); wac.setTempDirectory(tmpdir); @@ -64,7 +72,7 @@ public class WebAppStarter { } /** - * stop it + * stop it and remove the context * @throws just about anything, caller would be wise to catch Throwable */ static void stopWebApp(Server server, String appName) { @@ -74,6 +82,9 @@ public class WebAppStarter { // false -> not graceful wac.stop(false); } catch (InterruptedException ie) {} + try { + server.removeContext(wac); + } catch (IllegalStateException ise) {} } /** see comments in ConfigClientsHandler */ diff --git a/apps/routerconsole/java/strings/Strings.java b/apps/routerconsole/java/strings/Strings.java index 0805d479a119730e809bcd2d9a523af8194872d0..8d37b3b7638be04d7ba8a3317c4197da1c0177d1 100644 --- a/apps/routerconsole/java/strings/Strings.java +++ b/apps/routerconsole/java/strings/Strings.java @@ -71,5 +71,30 @@ class Dummy { _("Transport"); _("Tunnels"); _("udp"); + + // parameters in transport addresses (netdb.jsp) + // may or may not be worth translating + _("host"); + _("key"); + _("port"); + // capabilities + _("caps"); + // introducer host + _("ihost0"); + _("ihost1"); + _("ihost2"); + // introducer port + _("iport0"); + _("iport1"); + _("iport2"); + // introducer key + _("ikey0"); + _("ikey1"); + _("ikey2"); + // introducer tag + _("itag0"); + _("itag1"); + _("itag2"); + } } diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp index 4bb17007c6bf500d4dc523badff91afb5f1a6973..c9a668c79791eaf9b91b8480e7bf670264fcd700 100644 --- a/apps/routerconsole/jsp/configclients.jsp +++ b/apps/routerconsole/jsp/configclients.jsp @@ -30,7 +30,9 @@ button span.hide{ <% String prev = System.getProperty("net.i2p.router.web.ConfigClientsHandler.nonce"); if (prev != null) System.setProperty("net.i2p.router.web.ConfigClientsHandler.noncePrev", prev); System.setProperty("net.i2p.router.web.ConfigClientsHandler.nonce", new java.util.Random().nextLong()+""); %> - <input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigClientsHandler.nonce")%>" /> + <input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigClientsHandler.nonce")%>" > + <% /* set hidden default */ %> + <button type="submit" name="action" value="" style="display:none" >Cancel</button> <h3><%=intl._("Client Configuration")%></h3><p> <%=intl._("The Java clients listed below are started by the router and run in the same JVM.")%> </p><div class="wideload"> @@ -56,7 +58,7 @@ button span.hide{ <input type="submit" name="action" value="<%=intl._("Save WebApp Configuration")%>" /> </div></div> <% if (clientshelper.showPlugins()) { %> -<h3><a name="webapp"></a><%=intl._("Plugin Configuration")%></h3><p> +<h3><a name="pconfig"></a><%=intl._("Plugin Configuration")%></h3><p> <%=intl._("The plugins listed below are started by the webConsole client.")%> </p><div class="wideload"><p> <jsp:getProperty name="clientshelper" property="form3" /> diff --git a/apps/routerconsole/jsp/configupdate.jsp b/apps/routerconsole/jsp/configupdate.jsp index 017b7ba1579dd9fc1bfc2dc71b64ec35a10772da..22432f60ac3c65be55f72a77d353637d39c5eb84 100644 --- a/apps/routerconsole/jsp/configupdate.jsp +++ b/apps/routerconsole/jsp/configupdate.jsp @@ -25,7 +25,9 @@ <% String prev = System.getProperty("net.i2p.router.web.ConfigUpdateHandler.nonce"); if (prev != null) System.setProperty("net.i2p.router.web.ConfigUpdateHandler.noncePrev", prev); System.setProperty("net.i2p.router.web.ConfigUpdateHandler.nonce", new java.util.Random().nextLong()+""); %> - <input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigUpdateHandler.nonce")%>" /> + <input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigUpdateHandler.nonce")%>" > + <% /* set hidden default */ %> + <input type="submit" name="action" value="" style="display:none" > <h3><%=intl._("Check for I2P and news updates")%></h3> <div class="wideload"><table border="0" cellspacing="5"> <tr><td colspan="2"></tr> @@ -59,6 +61,6 @@ <% } // if canInstall %> <tr class="tablefooter"><td colspan="2"> <div class="formaction"> - <input type="submit" name="action" value="<%=intl._("Save")%>" /> - <input type="reset" value="<%=intl._("Cancel")%>" /> + <input type="reset" value="<%=intl._("Cancel")%>" > + <input type="submit" name="action" value="<%=intl._("Save")%>" > </div></td></tr></table></div></form></div></div></body></html> diff --git a/build.xml b/build.xml index 911c75d015b796d4c825093cfa61ca4c19d26dfd..df30347ddc9f83242788074aac55e7cf7c39a64d 100644 --- a/build.xml +++ b/build.xml @@ -264,8 +264,30 @@ </delete> </target> - <target name="preppkg" depends="preppkg-linux, buildexe"> + <target name="preppkg" depends="preppkg-linux, preppkg-windows"> <copy file="build/jbigi.jar" todir="pkg-temp/lib" /> + <copy todir="pkg-temp/lib/wrapper/freebsd/"> + <fileset dir="installer/lib/wrapper/freebsd/" /> + </copy> + <copy todir="pkg-temp/lib/wrapper/macosx/"> + <fileset dir="installer/lib/wrapper/macosx/" /> + </copy> + <copy todir="pkg-temp/lib/wrapper/solaris/"> + <fileset dir="installer/lib/wrapper/solaris/" /> + </copy> + </target> + + <target name="preppkg-windows-only" depends="preppkg-windows"> + <!-- rip the non-windows stuff out of jbigi.jar --> + <mkdir dir="tmpextract" /> + <unjar src="build/jbigi.jar" dest="tmpextract/" /> + <jar destfile="pkg-temp/lib/jbigi.jar" > + <fileset dir="tmpextract/" includes="*windows*" /> + </jar> + <delete dir="tmpextract/" /> + </target> + + <target name="preppkg-windows" depends="preppkg-base, buildexe"> <copy file="i2p.exe" todir="pkg-temp/" failonerror="false" /> <copy file="apps/systray/java/lib/systray4j.dll" todir="pkg-temp/lib" /> <copy file="apps/systray/java/resources/iggy.ico" todir="pkg-temp/icons" /> @@ -276,15 +298,6 @@ <copy file="installer/resources/install_i2p_service_winnt.bat" todir="pkg-temp/" /> <copy file="installer/resources/postinstall.bat" todir="pkg-temp/" /> <copy file="installer/resources/uninstall_i2p_service_winnt.bat" todir="pkg-temp/" /> - <copy todir="pkg-temp/lib/wrapper/freebsd/"> - <fileset dir="installer/lib/wrapper/freebsd/" /> - </copy> - <copy todir="pkg-temp/lib/wrapper/macosx/"> - <fileset dir="installer/lib/wrapper/macosx/" /> - </copy> - <copy todir="pkg-temp/lib/wrapper/solaris/"> - <fileset dir="installer/lib/wrapper/solaris/" /> - </copy> <copy todir="pkg-temp/lib/wrapper/win32/"> <fileset dir="installer/lib/wrapper/win32/" /> </copy> @@ -301,7 +314,22 @@ <delete dir="tmpextract/" /> </target> - <target name="preppkg-linux" depends="build, preplicenses, prepconsoleDocs"> + <target name="preppkg-linux" depends="preppkg-base"> + <copy file="installer/resources/runplain.sh" todir="pkg-temp/" /> + <copy file="apps/i2psnark/launch-i2psnark" todir="pkg-temp/" /> + <copy file="installer/resources/eepget" todir="pkg-temp/" /> + <copy file="installer/resources/i2prouter" todir="pkg-temp/" /> + <copy file="installer/resources/osid" todir="pkg-temp/" /> + <copy file="installer/resources/postinstall.sh" todir="pkg-temp/" /> + <copy todir="pkg-temp/lib/wrapper/linux/"> + <fileset dir="installer/lib/wrapper/linux/" /> + </copy> + <copy todir="pkg-temp/lib/wrapper/linux64/"> + <fileset dir="installer/lib/wrapper/linux64/" /> + </copy> + </target> + + <target name="preppkg-base" depends="build, preplicenses, prepconsoleDocs"> <copy file="build/i2p.jar" todir="pkg-temp/lib/" /> <copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" /> <copy file="build/jasper-compiler.jar" todir="pkg-temp/lib/" /> @@ -320,7 +348,6 @@ <copy file="build/BOB.jar" todir="pkg-temp/lib/" /> <copy file="build/systray.jar" todir="pkg-temp/lib" /> <copy file="build/i2psnark.jar" todir="pkg-temp/lib/" /> - <copy file="installer/resources/runplain.sh" todir="pkg-temp/" /> <copy file="apps/systray/java/lib/systray4j.jar" todir="pkg-temp/lib" /> <copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" /> <copy file="build/routerconsole.war" todir="pkg-temp/webapps/" /> @@ -330,24 +357,13 @@ <copy file="apps/susidns/src/WEB-INF/lib/jstl.jar" todir="pkg-temp/lib/" /> <copy file="apps/susidns/src/WEB-INF/lib/standard.jar" todir="pkg-temp/lib/" /> <copy file="build/i2psnark.war" todir="pkg-temp/webapps/" /> - <copy file="apps/i2psnark/launch-i2psnark" todir="pkg-temp/" /> <copy file="apps/i2psnark/jetty-i2psnark.xml" todir="pkg-temp/" /> <copy file="apps/i2psnark/i2psnark.config" todir="pkg-temp/" /> <copy file="installer/resources/blocklist.txt" todir="pkg-temp/" /> <copy file="installer/resources/clients.config" todir="pkg-temp/" /> - <copy file="installer/resources/eepget" todir="pkg-temp/" /> - <copy file="installer/resources/i2prouter" todir="pkg-temp/" /> <copy file="installer/resources/i2ptunnel.config" todir="pkg-temp/" /> - <copy file="installer/resources/osid" todir="pkg-temp/" /> - <copy file="installer/resources/postinstall.sh" todir="pkg-temp/" /> <copy file="installer/resources/systray.config" todir="pkg-temp/" /> <copy file="installer/resources/wrapper.config" todir="pkg-temp/" /> - <copy todir="pkg-temp/lib/wrapper/linux/"> - <fileset dir="installer/lib/wrapper/linux/" /> - </copy> - <copy todir="pkg-temp/lib/wrapper/linux64/"> - <fileset dir="installer/lib/wrapper/linux64/" /> - </copy> <copy file="installer/resources/hosts.txt" todir="pkg-temp/" /> <copy file="INSTALL-headless.txt" todir="pkg-temp/" /> <!-- overwrite the truncated history put in by the updater --> diff --git a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java index ff1b367fd055ae06fad46e10b94450f5688ecf49..a6045d6a27421d35258f0e2f478b018dd544c663 100644 --- a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java +++ b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java @@ -7,7 +7,11 @@ */ package net.i2p.client.naming; +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -87,13 +91,10 @@ public class HostsTxtNamingService extends NamingService { List filenames = getFilenames(); for (int i = 0; i < filenames.size(); i++) { String hostsfile = (String)filenames.get(i); - Properties hosts = new Properties(); try { File f = new File(_context.getRouterDir(), hostsfile); if ( (f.exists()) && (f.canRead()) ) { - DataHelper.loadProps(hosts, f, true); - - String key = hosts.getProperty(hostname.toLowerCase()); + String key = getKey(f, hostname.toLowerCase()); if ( (key != null) && (key.trim().length() > 0) ) { d = lookupBase64(key); putCache(hostname, d); @@ -168,4 +169,30 @@ public class HostsTxtNamingService extends NamingService { } return null; } + + /** + * Better than DataHelper.loadProps(), doesn't load the whole file into memory, + * and stops when it finds a match. + * + * @param host lower case + * @since 0.7.13 + */ + private static String getKey(File file, String host) throws IOException { + BufferedReader in = null; + try { + in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"), 16*1024); + String line = null; + while ( (line = in.readLine()) != null) { + if (!line.toLowerCase().startsWith(host + '=')) + continue; + if (line.indexOf('#') > 0) // trim off any end of line comment + line = line.substring(0, line.indexOf('#')).trim(); + int split = line.indexOf('='); + return line.substring(split+1); //.trim() ?????????????? + } + } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} + } + return null; + } } diff --git a/core/java/src/net/i2p/data/PrivateKeyFile.java b/core/java/src/net/i2p/data/PrivateKeyFile.java index c2c9d1c0abb89777324bea9ed2e0b6d9c2afa85d..dc445a37aeb420e2b068fb2a78c259e46314bf25 100644 --- a/core/java/src/net/i2p/data/PrivateKeyFile.java +++ b/core/java/src/net/i2p/data/PrivateKeyFile.java @@ -267,6 +267,8 @@ public class PrivateKeyFile { StringBuilder s = new StringBuilder(128); s.append("Dest: "); s.append(this.dest != null ? this.dest.toBase64() : "null"); + s.append("\nB32: "); + s.append(this.dest != null ? Base32.encode(this.dest.calculateHash().getData()) + ".b32.i2p" : "null"); s.append("\nContains: "); s.append(this.dest); s.append("\nPrivate Key: "); diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java index 2bfd8ccf12cfbdda1be685df762a2cc48be790b6..ca5c0d55a917bd09e204fa244e4d691dcd88e660 100644 --- a/core/java/src/net/i2p/util/EepGet.java +++ b/core/java/src/net/i2p/util/EepGet.java @@ -455,6 +455,8 @@ public class EepGet { _listeners.get(i).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe); if (_log.shouldLog(Log.WARN)) _log.warn("ERR: doFetch failed " + ioe); + if (ioe instanceof MalformedURLException) + _keepFetching = false; } finally { if (_out != null) { try { diff --git a/history.txt b/history.txt index e7edc851fe91004a0ae72b2bb8655eff8ffd37bb..b9c2c00bacbaf2ff8ceab8a77ba6d6ab6850b443 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,44 @@ +2010-04-02 zzz + * FloodfillPeerSelector: Adjust rankings again + +2010-03-31 zzz + * FloodfillPeerSelector: Adjust rankings to try to + improve LeaseSet lookups + * HostsTxtNamingService: Don't load the whole hosts.txt + into memory for every lookup + +2010-03-29 zzz + * build.xml: Prep for a windows-only pkg + * configclients.jsp: + - Always show start button for webapps and plugins + * configclients.jsp, configupdate.jsp: + - Fix submission when entering CR in a text box + * EepGet: Don't retry after a MalformedURLException + * HTTPResponseOutputStream: More static + * Plugins: + - Stop all plugins at shutdown + - Log tweaks + * WebApps: + - Remove the WAC after stopping it + - Stop a WAC before starting it to prevent dups + - Implement destroy() in addressbook to prevent dups + - Implement destroy() in i2psnark to prevent dups + +2010-03-25 zzz + * configclients.jsp: Fix dup anchor + * Console: Sort plugin links in summary bar + * i2psnark: + - Send numwant=0 if we don't need peers + - Report returned complete and incomplete counts + if higher than peer count + - Allow missing peer list + - Log tweaks + * netdb.jsp: Tag transport properties + * Plugins: Remove final check and install console + messages after a while + * PrivateKeyFile: Add b32 output + * Reseed: Add another host + 2010-03-18 zzz * Blocklist, CommSystem, FIFOBandwidth, TransportManager, OutNetMessage, InNetMessagePool: @@ -48,6 +89,10 @@ not sure why taking them from the tail "reduces latency" - Java 5 cleanup +2010-03-17 zzz + * I2PTunnel: Disable nonce checking when console password set + * Reseed: Add another host + * 2010-03-15 0.7.12 released 2010-03-13 zzz diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index b8546913b6cc18d89a6649558f7117b30b9aafa8..4e28a2c8d2676ff98ee569a93ae883ef407d9461 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -13,12 +13,12 @@ import net.i2p.CoreVersion; /** * Expose a version string * -n */ + */ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 1; + public final static long BUILD = 5; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java index 4d270424bd8ad16302a6534d30a475e645857ba2..9cacb0eed437c3da0621160adfd3579d3f4324c0 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java @@ -112,7 +112,7 @@ class FloodfillPeerSelector extends PeerSelector { private static final int NO_FAIL_STORE_OK = 10*60*1000; private static final int NO_FAIL_STORE_GOOD = NO_FAIL_STORE_OK * 2; /** this must be longer than the max streaming timeout (60s) */ - private static final int NO_FAIL_LOOKUP_OK = 2*60*1000; + private static final int NO_FAIL_LOOKUP_OK = 75*1000; private static final int NO_FAIL_LOOKUP_GOOD = NO_FAIL_LOOKUP_OK * 3; private static final int MAX_GOOD_RESP_TIME = 5*1000; @@ -135,8 +135,11 @@ class FloodfillPeerSelector extends PeerSelector { maxFailRate = 100d; // disable } + // 8 == FNDF.MAX_TO_FLOOD + 1 + int limit = Math.max(8, howMany); + limit = Math.min(limit, ffs.size()); // split sorted list into 3 sorted lists - for (int i = 0; found < howMany && i < ffs.size(); i++) { + for (int i = 0; found < howMany && i < limit; i++) { Hash entry = sorted.first(); sorted.remove(entry); if (entry == null) diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java index ca651bd49dbb8ef983f83a8e95222533baa74ac5..f38be9e14b4e3d230a9ad7a0edf87866b97d91a2 100644 --- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java +++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java @@ -36,7 +36,10 @@ public class Reseeder { // Reject unreasonably big files, because we download into a ByteArrayOutputStream. private static final long MAX_RESEED_RESPONSE_SIZE = 1024 * 1024; - private static final String DEFAULT_SEED_URL = "http://a.netdb.i2p2.de/,http://b.netdb.i2p2.de/,http://c.netdb.i2p2.de/,http://reseed.i2p-projekt.de/,http://i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/"; + private static final String DEFAULT_SEED_URL = + "http://a.netdb.i2p2.de/,http://b.netdb.i2p2.de/,http://c.netdb.i2p2.de/," + + "http://reseed.i2p-projekt.de/,http://i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/," + + "http://p2i.mine.nu/netDb/"; private static final String PROP_INPROGRESS = "net.i2p.router.web.ReseedHandler.reseedInProgress"; private static final String PROP_ERROR = "net.i2p.router.web.ReseedHandler.errorMessage"; private static final String PROP_STATUS = "net.i2p.router.web.ReseedHandler.statusMessage";