From 524a25eb2c5cdab6e3923a0748df9068174fcdfb Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 4 Jun 2009 19:14:40 +0000 Subject: [PATCH 01/58] Big directory rework. Eliminate all uses of the current working directory, and set up multiple directories specified by absolute paths for various uses. Add a WorkingDir class to create a user config directory and migrate files to it for new installs. The directory will be $HOME/.i2p on linux and %APPDIR%\I2P on Windows, or as specified in the system property -Di2p.dir.config=/path/to/i2pdir All files except for the base install and temp files will be in the config directory by default. Temp files will be in a i2p-xxxxx subdirectory of the system temp directory specified by the system property java.io.tmpdir. Convert all file opens in the code to be relative to a specific directory, as specified in the context. Code and applications should never open files relative to the current working directory (e.g. new File("foo")). All files should be accessed in the appropriate context directory, e.g. new File(_context.getAppDir(), "foo"). The router.config file location may be specified as a system property on the java command line with -Drouter.configLocation=/path/to/router.config All directories may be specified as properties in the router.config file. The migration will copy all files from an existing installation, except i2psnark/, with the system property -Di2p.dir.migrate=true. Otherwise it will just set up a new directory with a minimal configuration. The migration will also create a modified wrapper.config and (on linux only) a modified i2prouter script, and place them in the config directory. There are no changes to the installer or the default i2prouter, i2prouter.bat, i2prouter, wrapper.config, runplain.sh, windows service installer/uninstaller, etc. in this checkin. * Directories. These are all set at instantiation and will not be changed by * subsequent property changes. * All properties, if set, should be absolute paths. * * Name Property Method Files * ----- -------- ----- ----- * Base i2p.dir.base getBaseDir() lib/, webapps/, docs/, geoip/, licenses/, ... * Temp i2p.dir.temp getTempDir() Temporary files * Config i2p.dir.config getConfigDir() *.config, hosts.txt, addressbook/, ... * * (the following all default to the same as Config) * * Router i2p.dir.router getRouterDir() netDb/, peerProfiles/, router.*, keyBackup/, ... * Log i2p.dir.log getLogDir() wrapper.log*, logs/ * PID i2p.dir.pid getPIDDir() wrapper *.pid files, router.ping * App i2p.dir.app getAppDir() eepsite/, ... * * Note that we can't control where the wrapper actually puts its files. All these will be set appropriately in a Router Context. In an I2P App Context, all except Temp will be the current working directory. Lightly tested so far, needs much more testing. --- apps/BOB/src/net/i2p/BOB/BOB.java | 23 +- .../java/src/addressbook/AddressBook.java | 7 +- .../java/src/addressbook/Daemon.java | 13 +- .../src/org/klomp/snark/I2PSnarkUtil.java | 2 +- .../src/org/klomp/snark/SnarkManager.java | 23 +- .../org/klomp/snark/web/I2PSnarkServlet.java | 1 + .../java/src/net/i2p/i2ptunnel/I2PTunnel.java | 8 + .../i2p/i2ptunnel/I2PTunnelConnectClient.java | 11 +- .../i2p/i2ptunnel/I2PTunnelHTTPClient.java | 18 +- .../net/i2p/i2ptunnel/TunnelController.java | 2 + .../i2p/i2ptunnel/TunnelControllerGroup.java | 6 +- .../i2p/router/web/ConfigServiceHandler.java | 2 +- .../src/net/i2p/router/web/ContentHelper.java | 4 +- .../src/net/i2p/router/web/LogsHelper.java | 70 +-- .../src/net/i2p/router/web/NewsFetcher.java | 39 +- .../src/net/i2p/router/web/ReseedHandler.java | 2 +- .../i2p/router/web/RouterConsoleRunner.java | 20 +- .../src/net/i2p/router/web/UpdateHandler.java | 11 +- apps/routerconsole/jsp/flags.jsp | 5 +- apps/routerconsole/jsp/viewtheme.jsp | 6 +- .../src/java/src/i2p/susi/dns/ConfigBean.java | 6 +- apps/susimail/build.xml | 1 + .../src/src/i2p/susi/util/Config.java | 6 +- .../freenet/support/CPUInformation/CPUID.java | 7 +- core/java/src/net/i2p/I2PAppContext.java | 150 ++++++ .../client/naming/HostsTxtNamingService.java | 6 +- .../src/net/i2p/crypto/TrustedUpdate.java | 21 +- .../java/src/net/i2p/data/PrivateKeyFile.java | 4 + .../src/net/i2p/stat/BufferedStatLog.java | 4 + core/java/src/net/i2p/util/FileUtil.java | 6 + core/java/src/net/i2p/util/LogManager.java | 32 +- core/java/src/net/i2p/util/LogWriter.java | 30 +- .../src/net/i2p/util/NativeBigInteger.java | 5 +- core/java/src/net/i2p/util/RandomSource.java | 4 +- core/java/src/net/i2p/util/WorkingDir.java | 481 ++++++++++++++++++ router/java/src/net/i2p/router/Blocklist.java | 4 + .../java/src/net/i2p/router/KeyManager.java | 6 +- router/java/src/net/i2p/router/Router.java | 138 ++++- .../src/net/i2p/router/RouterContext.java | 8 +- .../kademlia/PersistentDataStore.java | 2 +- .../peermanager/ProfilePersistenceHelper.java | 14 +- .../i2p/router/startup/ClientAppConfig.java | 7 +- .../router/startup/CreateRouterInfoJob.java | 17 +- .../i2p/router/startup/LoadRouterInfoJob.java | 16 +- .../router/startup/RebuildRouterInfoJob.java | 29 +- .../src/net/i2p/router/transport/GeoIP.java | 6 +- 46 files changed, 1039 insertions(+), 244 deletions(-) create mode 100644 core/java/src/net/i2p/util/WorkingDir.java diff --git a/apps/BOB/src/net/i2p/BOB/BOB.java b/apps/BOB/src/net/i2p/BOB/BOB.java index 9f87e4f88..1aa432e2e 100644 --- a/apps/BOB/src/net/i2p/BOB/BOB.java +++ b/apps/BOB/src/net/i2p/BOB/BOB.java @@ -23,6 +23,7 @@ */ package net.i2p.BOB; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -34,6 +35,8 @@ import java.net.Socket; import java.net.SocketTimeoutException; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; + +import net.i2p.I2PAppContext; import net.i2p.client.I2PClient; import net.i2p.client.streaming.RetransmissionTimer; import net.i2p.util.Log; @@ -186,16 +189,19 @@ public class BOB { i = Y2.hashCode(); try { { + File cfg = new File(configLocation); + if (!cfg.isAbsolute()) + cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation); try { - FileInputStream fi = new FileInputStream(configLocation); + FileInputStream fi = new FileInputStream(cfg); props.load(fi); fi.close(); } catch (FileNotFoundException fnfe) { - warn("Unable to load up the BOB config file " + configLocation + ", Using defaults."); + warn("Unable to load up the BOB config file " + cfg.getAbsolutePath() + ", Using defaults."); warn(fnfe.toString()); save = true; } catch (IOException ioe) { - warn("IOException on BOB config file " + configLocation + ", using defaults."); + warn("IOException on BOB config file " + cfg.getAbsolutePath() + ", using defaults."); warn(ioe.toString()); } } @@ -228,13 +234,16 @@ public class BOB { props.setProperty(PROP_BOB_HOST, "localhost"); } if (save) { + File cfg = new File(configLocation); + if (!cfg.isAbsolute()) + cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), configLocation); try { - warn("Writing new defaults file " + configLocation); - FileOutputStream fo = new FileOutputStream(configLocation); - props.store(fo, configLocation); + warn("Writing new defaults file " + cfg.getAbsolutePath()); + FileOutputStream fo = new FileOutputStream(cfg); + props.store(fo, cfg.getAbsolutePath()); fo.close(); } catch (IOException ioe) { - error("IOException on BOB config file " + configLocation + ", " + ioe); + error("IOException on BOB config file " + cfg.getAbsolutePath() + ", " + ioe); } } diff --git a/apps/addressbook/java/src/addressbook/AddressBook.java b/apps/addressbook/java/src/addressbook/AddressBook.java index a46c256c8..2694cae78 100644 --- a/apps/addressbook/java/src/addressbook/AddressBook.java +++ b/apps/addressbook/java/src/addressbook/AddressBook.java @@ -94,20 +94,21 @@ public class AddressBook { * @param proxyPort port number of proxy */ public AddressBook(Subscription subscription, String proxyHost, int proxyPort) { + File tmp = new File(I2PAppContext.getGlobalContext().getTempDir(), "addressbook.tmp"); this.location = subscription.getLocation(); EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true, - proxyHost, proxyPort, 0, -1l, MAX_SUB_SIZE, "addressbook.tmp", null, + proxyHost, proxyPort, 0, -1l, MAX_SUB_SIZE, tmp.getAbsolutePath(), null, subscription.getLocation(), true, subscription.getEtag(), subscription.getLastModified(), null); if (get.fetch()) { subscription.setEtag(get.getETag()); subscription.setLastModified(get.getLastModified()); } try { - this.addresses = ConfigParser.parse(new File("addressbook.tmp")); + this.addresses = ConfigParser.parse(tmp); } catch (IOException exp) { this.addresses = new HashMap(); } - new File("addressbook.tmp").delete(); + tmp.delete(); } /** diff --git a/apps/addressbook/java/src/addressbook/Daemon.java b/apps/addressbook/java/src/addressbook/Daemon.java index a1b1ae18a..a8a9a2da6 100644 --- a/apps/addressbook/java/src/addressbook/Daemon.java +++ b/apps/addressbook/java/src/addressbook/Daemon.java @@ -28,6 +28,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import net.i2p.I2PAppContext; + /** * Main class of addressbook. Performs updates, and runs the main loop. * @@ -125,11 +127,13 @@ public class Daemon { public void run(String[] args) { String settingsLocation = "config.txt"; - String home; + File homeFile; if (args.length > 0) { - home = args[0]; + homeFile = new File(args[0]); + if (!homeFile.isAbsolute()) + homeFile = new File(I2PAppContext.getGlobalContext().getRouterDir(), args[0]); } else { - home = "."; + homeFile = new File(System.getProperty("user.dir")); } Map defaultSettings = new HashMap(); @@ -145,7 +149,6 @@ public class Daemon { defaultSettings.put("last_modified", "last_modified"); defaultSettings.put("update_delay", "12"); - File homeFile = new File(home); if (!homeFile.exists()) { boolean created = homeFile.mkdirs(); if (created) @@ -169,7 +172,7 @@ public class Daemon { delay = 1; } - update(settings, home); + update(settings, homeFile.getAbsolutePath()); try { synchronized (this) { wait(delay * 60 * 60 * 1000); diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index b7e623060..1a43514de 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -73,7 +73,7 @@ public class I2PSnarkUtil { // This is used for both announce replies and .torrent file downloads, // so it must be available even if not connected to I2CP. // so much for multiple instances - _tmpDir = new File("tmp", "i2psnark"); + _tmpDir = new File(ctx.getTempDir(), "i2psnark"); FileUtil.rmdir(_tmpDir, false); _tmpDir.mkdirs(); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index a45bf5161..79ea62ebc 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -31,7 +31,7 @@ public class SnarkManager implements Snark.CompleteListener { /** map of (canonical) filename to Snark instance (unsynchronized) */ private Map _snarks; private Object _addSnarkLock; - private String _configFile = "i2psnark.config"; + private File _configFile; private Properties _config; private I2PAppContext _context; private Log _log; @@ -51,6 +51,7 @@ public class SnarkManager implements Snark.CompleteListener { public static final String PROP_META_PREFIX = "i2psnark.zmeta."; public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield"; + private static final String CONFIG_FILE = "i2psnark.config"; public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops public static final String DEFAULT_AUTO_START = "false"; public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix"; @@ -66,6 +67,9 @@ public class SnarkManager implements Snark.CompleteListener { _log = _context.logManager().getLog(SnarkManager.class); _messages = new ArrayList(16); _util = new I2PSnarkUtil(_context); + _configFile = new File(CONFIG_FILE); + if (!_configFile.isAbsolute()) + _configFile = new File(_context.getConfigDir(), CONFIG_FILE); loadConfig(null); } @@ -112,10 +116,11 @@ public class SnarkManager implements Snark.CompleteListener { } private int getStartupDelayMinutes() { return 3; } public File getDataDir() { - String dir = _config.getProperty(PROP_DIR); - if ( (dir == null) || (dir.trim().length() <= 0) ) - dir = "i2psnark"; - return new File(dir); + String dir = _config.getProperty(PROP_DIR, "i2psnark"); + File f = new File(dir); + if (!f.isAbsolute()) + f = new File(_context.getAppDir(), dir); + return f; } /** null to set initial defaults */ @@ -123,8 +128,10 @@ public class SnarkManager implements Snark.CompleteListener { if (_config == null) _config = new Properties(); if (filename != null) { - _configFile = filename; File cfg = new File(filename); + if (!cfg.isAbsolute()) + cfg = new File(_context.getConfigDir(), filename); + _configFile = cfg; if (cfg.exists()) { try { DataHelper.loadProps(_config, cfg); @@ -352,10 +359,10 @@ public class SnarkManager implements Snark.CompleteListener { public void saveConfig() { try { synchronized (_configFile) { - DataHelper.storeProps(_config, new File(_configFile)); + DataHelper.storeProps(_config, _configFile); } } catch (IOException ioe) { - addMessage("Unable to save the config to '" + _configFile + "'"); + addMessage("Unable to save the config to '" + _configFile.getAbsolutePath() + "'"); } } 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 3923484a8..410e804d9 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -173,6 +173,7 @@ public class I2PSnarkServlet extends HttpServlet { } else if ("Add torrent".equals(action)) { String newFile = req.getParameter("newFile"); String newURL = req.getParameter("newURL"); + // NOTE - newFile currently disabled in HTML form - see below File f = null; if ( (newFile != null) && (newFile.trim().length() > 0) ) f = new File(newFile.trim()); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index 486bcdead..e4f8453bc 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -372,6 +372,8 @@ public class I2PTunnel implements Logging, EventDispatcher { } privKeyFile = new File(args[2]); + if (!privKeyFile.isAbsolute()) + privKeyFile = new File(_context.getAppDir(), args[2]); if (!privKeyFile.canRead()) { l.log("private key file does not exist"); _log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[2]); @@ -419,6 +421,8 @@ public class I2PTunnel implements Logging, EventDispatcher { } privKeyFile = new File(args[2]); + if (!privKeyFile.isAbsolute()) + privKeyFile = new File(_context.getAppDir(), args[2]); if (!privKeyFile.canRead()) { l.log("private key file does not exist"); _log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[2]); @@ -476,6 +480,8 @@ public class I2PTunnel implements Logging, EventDispatcher { String spoofedHost = args[2]; privKeyFile = new File(args[3]); + if (!privKeyFile.isAbsolute()) + privKeyFile = new File(_context.getAppDir(), args[3]); if (!privKeyFile.canRead()) { l.log("private key file does not exist"); _log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]); @@ -870,6 +876,8 @@ public class I2PTunnel implements Logging, EventDispatcher { } File privKeyFile = new File(args[1]); + if (!privKeyFile.isAbsolute()) + privKeyFile = new File(_context.getAppDir(), args[1]); if (!privKeyFile.canRead()) { l.log("private key file does not exist"); _log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java index 59971e8f3..79270303b 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java @@ -3,6 +3,7 @@ */ package net.i2p.i2ptunnel; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -106,6 +107,8 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna /** used to assign unique IDs to the threads / clients. no logic or functionality */ private static volatile long __clientId = 0; + private static final File _errorDir = new File(I2PAppContext.getGlobalContext().getBaseDir(), "docs"); + /** * @throws IllegalArgumentException if the I2PTunnel does not contain * valid config to contact the router @@ -261,9 +264,9 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna String str; byte[] header; if (usingWWWProxy) - str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnfp-header.ht")).getAbsolutePath(), 100, true); else - str = FileUtil.readTextFile("docs/dnfh-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnfh-header.ht")).getAbsolutePath(), 100, true); if (str != null) header = str.getBytes(); else @@ -357,9 +360,9 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna String str; byte[] header; if (usingWWWProxy) - str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnfp-header.ht")).getAbsolutePath(), 100, true); else - str = FileUtil.readTextFile("docs/dnf-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnf-header.ht")).getAbsolutePath(), 100, true); if (str != null) header = str.getBytes(); else diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 7e16114ad..cab438991 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -4,6 +4,7 @@ package net.i2p.i2ptunnel; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -136,6 +137,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable /** used to assign unique IDs to the threads / clients. no logic or functionality */ private static volatile long __clientId = 0; + private static final File _errorDir = new File(I2PAppContext.getGlobalContext().getBaseDir(), "docs"); + + /** * @throws IllegalArgumentException if the I2PTunnel does not contain * valid config to contact the router @@ -372,7 +376,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable { String str; byte[] header; - str = FileUtil.readTextFile("docs/ahelper-conflict-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "ahelper-conflict-header.ht")).getAbsolutePath(), 100, true); if (str != null) header = str.getBytes(); else header = ERR_AHELPER_CONFLICT; @@ -558,13 +562,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable byte[] header; boolean showAddrHelper = false; if (usingWWWProxy) - str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnfp-header.ht")).getAbsolutePath(), 100, true); else if(ahelper != 0) - str = FileUtil.readTextFile("docs/dnfb-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnfb-header.ht")).getAbsolutePath(), 100, true); else if (destination.length() == 60 && destination.endsWith(".b32.i2p")) - str = FileUtil.readTextFile("docs/dnf-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnf-header.ht")).getAbsolutePath(), 100, true); else { - str = FileUtil.readTextFile("docs/dnfh-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnfh-header.ht")).getAbsolutePath(), 100, true); showAddrHelper = true; } if (str != null) @@ -733,9 +737,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable String str; byte[] header; if (usingWWWProxy) - str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnfp-header.ht")).getAbsolutePath(), 100, true); else - str = FileUtil.readTextFile("docs/dnf-header.ht", 100, true); + str = FileUtil.readTextFile((new File(_errorDir, "dnf-header.ht")).getAbsolutePath(), 100, true); if (str != null) header = str.getBytes(); else diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 1f67783e9..3dbcfea30 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -72,6 +72,8 @@ public class TunnelController implements Logging { } File keyFile = new File(getPrivKeyFile()); + if (!keyFile.isAbsolute()) + keyFile = new File(I2PAppContext.getGlobalContext().getAppDir(), getPrivKeyFile()); if (keyFile.exists()) { //log("Not overwriting existing private keys in " + keyFile.getAbsolutePath()); return; diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java index 16f418be9..a0b8ea3f7 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java @@ -270,9 +270,11 @@ public class TunnelControllerGroup { */ private Properties loadConfig(String configFile) { File cfgFile = new File(configFile); + if (!cfgFile.isAbsolute()) + cfgFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), configFile); if (!cfgFile.exists()) { if (_log.shouldLog(Log.ERROR)) - _log.error("Unable to load the controllers from " + configFile); + _log.error("Unable to load the controllers from " + cfgFile.getAbsolutePath()); return null; } @@ -282,7 +284,7 @@ public class TunnelControllerGroup { return props; } catch (IOException ioe) { if (_log.shouldLog(Log.ERROR)) - _log.error("Error reading the controllers from " + configFile, ioe); + _log.error("Error reading the controllers from " + cfgFile.getAbsolutePath(), ioe); return null; } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java index 0dc4d1e62..195889fad 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java @@ -41,7 +41,7 @@ public class ConfigServiceHandler extends FormHandler { } public void run() { try { - Router.killKeys(); + ContextHelper.getContext(null).router().killKeys(); WrapperManager.signalStopped(_exitCode); } catch (Throwable t) { t.printStackTrace(); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ContentHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ContentHelper.java index ce29250b9..721655a51 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ContentHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ContentHelper.java @@ -3,7 +3,6 @@ package net.i2p.router.web; import java.io.File; import java.util.Locale; -import net.i2p.router.RouterContext; import net.i2p.util.FileUtil; public class ContentHelper extends HelperBase { @@ -14,6 +13,9 @@ public class ContentHelper extends HelperBase { public ContentHelper() {} + /** + * Caution, use absolute paths only, do not assume files are in CWD + */ public void setPage(String page) { _page = page; } public void setStartAtBeginning(String moo) { _startAtBeginning = Boolean.valueOf(""+moo).booleanValue(); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java index e1fce8f3e..a4896cdb8 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java @@ -1,66 +1,70 @@ package net.i2p.router.web; +import java.io.File; import java.util.List; -import net.i2p.router.RouterContext; import net.i2p.util.FileUtil; public class LogsHelper extends HelperBase { public LogsHelper() {} public String getLogs() { - List msgs = _context.logManager().getBuffer().getMostRecentMessages(); - StringBuffer buf = new StringBuffer(16*1024); - buf.append("\n"); - - return buf.toString(); + return formatMessages(_context.logManager().getBuffer().getMostRecentMessages()); } public String getCriticalLogs() { - List msgs = _context.logManager().getBuffer().getMostRecentCriticalMessages(); - StringBuffer buf = new StringBuffer(16*1024); - buf.append("\n"); - - return buf.toString(); + return formatMessages(_context.logManager().getBuffer().getMostRecentCriticalMessages()); } public String getServiceLogs() { - String str = FileUtil.readTextFile("wrapper.log", 250, false); + // look in new and old place + File f = new File(_context.getLogDir(), "wrapper.log"); + if (!f.exists()) + f = new File(_context.getBaseDir(), "wrapper.log"); + String str = FileUtil.readTextFile(f.getAbsolutePath(), 250, false); if (str == null) return ""; else { - str = str.replaceAll("<","<"); + str = str.replaceAll("<", "<").replaceAll(">", ">"); return "
" + str + "
"; } } + /***** unused public String getConnectionLogs() { - List msgs = _context.commSystem().getMostRecentErrorMessages(); + return formatMessages(_context.commSystem().getMostRecentErrorMessages()); + } + ******/ + + private String formatMessages(List msgs) { + boolean colorize = Boolean.valueOf(_context.getProperty("routerconsole.logs.color")).booleanValue(); StringBuffer buf = new StringBuffer(16*1024); buf.append("\n"); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java index 98fac325d..fd7662caf 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java @@ -27,6 +27,8 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { private long _lastUpdated; private String _updateVersion; private String _lastModified; + private File _newsFile; + private File _tempFile; private static NewsFetcher _instance; //public static final synchronized NewsFetcher getInstance() { return _instance; } public static final synchronized NewsFetcher getInstance(I2PAppContext ctx) { @@ -35,25 +37,26 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { _instance = new NewsFetcher(ctx); return _instance; } - + private static final String NEWS_FILE = "docs/news.xml"; - private static final String TEMP_NEWS_FILE = "docs/news.xml.temp"; + private static final String TEMP_NEWS_FILE = "news.xml.temp"; private NewsFetcher(I2PAppContext ctx) { _context = ctx; _log = ctx.logManager().getLog(NewsFetcher.class); _instance = this; _lastFetch = 0; + _newsFile = new File(_context.getBaseDir(), NEWS_FILE); + _tempFile = new File(_context.getTempDir(), TEMP_NEWS_FILE); updateLastFetched(); _lastUpdated = _lastFetch; _updateVersion = ""; } private void updateLastFetched() { - File news = new File(NEWS_FILE); - if (news.exists()) { + if (_newsFile.exists()) { if (_lastFetch == 0) - _lastFetch = news.lastModified(); + _lastFetch = _newsFile.lastModified(); } else _lastFetch = 0; } @@ -82,7 +85,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY); if ("notify".equals(policy)) return false; - File zip = new File(Router.UPDATE_FILE); + File zip = new File(_context.getRouterDir(), Router.UPDATE_FILE); return !zip.exists(); } @@ -114,18 +117,17 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue(); String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT); - File tempFile = new File(TEMP_NEWS_FILE); - if (tempFile.exists()) - tempFile.delete(); + if (_tempFile.exists()) + _tempFile.delete(); int proxyPort = -1; try { proxyPort = Integer.parseInt(port); EepGet get = null; if (shouldProxy) - get = new EepGet(_context, true, proxyHost, proxyPort, 2, TEMP_NEWS_FILE, newsURL, true, null, _lastModified); + get = new EepGet(_context, true, proxyHost, proxyPort, 2, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified); else - get = new EepGet(_context, false, null, 0, 0, TEMP_NEWS_FILE, newsURL, true, null, _lastModified); + get = new EepGet(_context, false, null, 0, 0, _tempFile.getAbsolutePath(), newsURL, true, null, _lastModified); get.addStatusListener(this); if (get.fetch()) _lastModified = get.getLastModified(); @@ -138,11 +140,10 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { private static final String VERSION_PREFIX = "version=\""; private void checkForUpdates() { _updateAvailable = false; - File news = new File(NEWS_FILE); - if ( (!news.exists()) || (news.length() <= 0) ) return; + if ( (!_newsFile.exists()) || (_newsFile.length() <= 0) ) return; FileInputStream in = null; try { - in = new FileInputStream(news); + in = new FileInputStream(_newsFile); StringBuffer buf = new StringBuffer(128); while (DataHelper.readLine(in, buf)) { int index = buf.indexOf(VERSION_PREFIX); @@ -220,13 +221,12 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { if (_log.shouldLog(Log.INFO)) _log.info("News fetched from " + url + " with " + (alreadyTransferred+bytesTransferred)); - File temp = new File(TEMP_NEWS_FILE); long now = _context.clock().now(); - if (temp.exists()) { - boolean copied = FileUtil.copy(TEMP_NEWS_FILE, NEWS_FILE, true); + if (_tempFile.exists()) { + boolean copied = FileUtil.copy(_tempFile.getAbsolutePath(), _newsFile.getAbsolutePath(), true); if (copied) { _lastUpdated = now; - temp.delete(); + _tempFile.delete(); checkForUpdates(); } else { if (_log.shouldLog(Log.ERROR)) @@ -242,8 +242,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) { if (_log.shouldLog(Log.WARN)) _log.warn("Failed to fetch the news from " + url); - File temp = new File(TEMP_NEWS_FILE); - temp.delete(); + _tempFile.delete(); } public void headerReceived(String url, int attemptNum, String key, String val) {} public void attempting(String url) {} diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java index e701ede99..c11c6150c 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java @@ -256,7 +256,7 @@ public class ReseedHandler { private void writeSeed(String name, byte data[]) throws Exception { String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb"); - File netDbDir = new File(dirName); + File netDbDir = new File(_context.getRouterDir(), dirName); if (!netDbDir.exists()) { boolean ok = netDbDir.mkdirs(); } 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 862902b1f..f58aeb308 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -59,7 +59,7 @@ public class RouterConsoleRunner { } public void startConsole() { - File workDir = new File("work"); + File workDir = new File(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work"); boolean workDirRemoved = FileUtil.rmdir(workDir, false); if (!workDirRemoved) System.err.println("ERROR: Unable to remove Jetty temporary work directory"); @@ -95,8 +95,11 @@ public class RouterConsoleRunner { } _server.setRootWebApp(ROUTERCONSOLE); WebApplicationContext wac = _server.addWebApplication("/", _webAppsDir + ROUTERCONSOLE + ".war"); + File tmpdir = new File(workDir, ROUTERCONSOLE + "-" + _listenPort); + tmpdir.mkdir(); + wac.setTempDirectory(tmpdir); initialize(wac); - File dir = new File(_webAppsDir); + File dir = new File(I2PAppContext.getGlobalContext().getBaseDir(), _webAppsDir); String fileNames[] = dir.list(WarFilenameFilter.instance()); if (fileNames != null) { for (int i = 0; i < fileNames.length; i++) { @@ -106,6 +109,9 @@ public class RouterConsoleRunner { if (! "false".equals(enabled)) { String path = new File(dir, fileNames[i]).getCanonicalPath(); wac = _server.addWebApplication("/"+ appName, path); + tmpdir = new File(workDir, appName + "-" + _listenPort); + tmpdir.mkdir(); + wac.setTempDirectory(tmpdir); initialize(wac); if (enabled == null) { // do this so configclients.jsp knows about all apps from reading the config @@ -144,10 +150,10 @@ public class RouterConsoleRunner { // don't have an installation directory that they can put the flag in yet. File noReseedFile = new File(new File(System.getProperty("user.home")), ".i2pnoreseed"); File noReseedFileAlt1 = new File(new File(System.getProperty("user.home")), "noreseed.i2p"); - File noReseedFileAlt2 = new File(".i2pnoreseed"); - File noReseedFileAlt3 = new File("noreseed.i2p"); + File noReseedFileAlt2 = new File(I2PAppContext.getGlobalContext().getConfigDir(), ".i2pnoreseed"); + File noReseedFileAlt3 = new File(I2PAppContext.getGlobalContext().getConfigDir(), "noreseed.i2p"); if (!noReseedFile.exists() && !noReseedFileAlt1.exists() && !noReseedFileAlt2.exists() && !noReseedFileAlt3.exists()) { - File netDb = new File("netDb"); + File netDb = new File(I2PAppContext.getGlobalContext().getRouterDir(), "netDb"); // sure, some of them could be "my.info" or various leaseSet- files, but chances are, // if someone has those files, they've already been seeded (at least enough to let them // get i2p started - they can reseed later in the web console) @@ -216,7 +222,7 @@ public class RouterConsoleRunner { Properties rv = new Properties(); // String webappConfigFile = ctx.getProperty(PROP_WEBAPP_CONFIG_FILENAME, DEFAULT_WEBAPP_CONFIG_FILENAME); String webappConfigFile = DEFAULT_WEBAPP_CONFIG_FILENAME; - File cfgFile = new File(webappConfigFile); + File cfgFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), webappConfigFile); try { DataHelper.loadProps(rv, cfgFile); @@ -230,7 +236,7 @@ public class RouterConsoleRunner { public static void storeWebAppProperties(Properties props) { // String webappConfigFile = ctx.getProperty(PROP_WEBAPP_CONFIG_FILENAME, DEFAULT_WEBAPP_CONFIG_FILENAME); String webappConfigFile = DEFAULT_WEBAPP_CONFIG_FILENAME; - File cfgFile = new File(webappConfigFile); + File cfgFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), webappConfigFile); try { DataHelper.storeProps(props, cfgFile); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java index 35edadfb6..267fe5a07 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java @@ -31,6 +31,7 @@ public class UpdateHandler { protected RouterContext _context; protected Log _log; protected DecimalFormat _pct = new DecimalFormat("00.0%"); + protected String _updateFile; protected static final String SIGNED_UPDATE_FILE = "i2pupdate.sud"; protected static final String PROP_UPDATE_IN_PROGRESS = "net.i2p.router.web.UpdateHandler.updateInProgress"; @@ -41,6 +42,7 @@ public class UpdateHandler { public UpdateHandler(RouterContext ctx) { _context = ctx; _log = ctx.logManager().getLog(UpdateHandler.class); + _updateFile = (new File(ctx.getRouterDir(), SIGNED_UPDATE_FILE)).getAbsolutePath(); } /** @@ -137,9 +139,9 @@ public class UpdateHandler { try { EepGet get = null; if (shouldProxy) - get = new EepGet(_context, proxyHost, proxyPort, 20, SIGNED_UPDATE_FILE, updateURL, false); + get = new EepGet(_context, proxyHost, proxyPort, 20, _updateFile, updateURL, false); else - get = new EepGet(_context, 1, SIGNED_UPDATE_FILE, updateURL, false); + get = new EepGet(_context, 1, _updateFile, updateURL, false); get.addStatusListener(UpdateRunner.this); get.fetch(); } catch (Throwable t) { @@ -167,8 +169,9 @@ public class UpdateHandler { public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { _status = "Update downloaded"; TrustedUpdate up = new TrustedUpdate(_context); - String err = up.migrateVerified(RouterVersion.VERSION, SIGNED_UPDATE_FILE, Router.UPDATE_FILE); - File f = new File(SIGNED_UPDATE_FILE); + File f = new File(_updateFile); + File to = new File(_context.getBaseDir(), Router.UPDATE_FILE); + String err = up.migrateVerified(RouterVersion.VERSION, f, to); f.delete(); if (err == null) { String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY); diff --git a/apps/routerconsole/jsp/flags.jsp b/apps/routerconsole/jsp/flags.jsp index 38d068d59..c990ba27a 100644 --- a/apps/routerconsole/jsp/flags.jsp +++ b/apps/routerconsole/jsp/flags.jsp @@ -15,8 +15,11 @@ if (c != null && c.length() > 0) { java.io.OutputStream cout = response.getOutputStream(); response.setContentType("image/png"); response.setHeader("Cache-Control", "max-age=86400"); // cache for a day + String base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath(); + String file = "docs" + java.io.File.separatorChar + "icons" + java.io.File.separatorChar + + "flags" + java.io.File.separatorChar + c + ".png"; try { - net.i2p.util.FileUtil.readFile(c + ".png", "docs/icons/flags", cout); + net.i2p.util.FileUtil.readFile(file, base, cout); rendered = true; } catch (java.io.IOException ioe) {} if (rendered) diff --git a/apps/routerconsole/jsp/viewtheme.jsp b/apps/routerconsole/jsp/viewtheme.jsp index 05ccfecbf..5785195c2 100644 --- a/apps/routerconsole/jsp/viewtheme.jsp +++ b/apps/routerconsole/jsp/viewtheme.jsp @@ -1,4 +1,4 @@ -<% +% /* * USE CAUTION WHEN EDITING * Trailing whitespace OR NEWLINE on the last line will cause @@ -16,5 +16,7 @@ if (uri.endsWith(".css")) { response.setContentType("image/jpeg"); } -net.i2p.util.FileUtil.readFile(uri, "./docs", response.getOutputStream()); +String base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath() + + java.io.File.separatorChar + "docs"; +net.i2p.util.FileUtil.readFile(uri, base, response.getOutputStream()); %> \ No newline at end of file diff --git a/apps/susidns/src/java/src/i2p/susi/dns/ConfigBean.java b/apps/susidns/src/java/src/i2p/susi/dns/ConfigBean.java index 15a2204f8..8cddafdc4 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/ConfigBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/ConfigBean.java @@ -33,12 +33,16 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.Serializable; +import net.i2p.I2PAppContext; + public class ConfigBean implements Serializable { /* * as this is not provided as constant in addressbook, we define it here */ - public static final String addressbookPrefix = "addressbook/"; + public static final String addressbookPrefix = + (new File(I2PAppContext.getGlobalContext().getRouterDir(), "addressbook")).getAbsolutePath() + + File.separatorChar; public static final String configFileName = addressbookPrefix + "config.txt"; private String action, config; diff --git a/apps/susimail/build.xml b/apps/susimail/build.xml index 886f9471f..fb4371614 100644 --- a/apps/susimail/build.xml +++ b/apps/susimail/build.xml @@ -15,6 +15,7 @@ + diff --git a/apps/susimail/src/src/i2p/susi/util/Config.java b/apps/susimail/src/src/i2p/susi/util/Config.java index 219ce71f3..723c56b99 100644 --- a/apps/susimail/src/src/i2p/susi/util/Config.java +++ b/apps/susimail/src/src/i2p/susi/util/Config.java @@ -25,10 +25,13 @@ package i2p.susi.util; import i2p.susi.debug.Debug; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; +import net.i2p.I2PAppContext; + /** * @author susi */ @@ -81,7 +84,8 @@ public class Config { } FileInputStream fis = null; try { - fis = new FileInputStream( "susimail.config" ); + File cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), "susimail.config"); + fis = new FileInputStream(cfg); config.load( fis ); } catch (Exception e) { Debug.debug( Debug.DEBUG, "Could not open susimail.config, reason: " + e.getMessage() ); diff --git a/core/java/src/freenet/support/CPUInformation/CPUID.java b/core/java/src/freenet/support/CPUInformation/CPUID.java index 1d5c84882..58469c6d7 100644 --- a/core/java/src/freenet/support/CPUInformation/CPUID.java +++ b/core/java/src/freenet/support/CPUInformation/CPUID.java @@ -9,6 +9,8 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import net.i2p.I2PAppContext; + /** * @author Iakin * A class for retrieveing details about the CPU using the CPUID assembly instruction. @@ -503,9 +505,10 @@ public class CPUID { FileOutputStream fos = null; try { InputStream libStream = resource.openStream(); - outFile = new File(libPrefix + "jcpuid" + libSuffix); + outFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), libPrefix + "jcpuid" + libSuffix); fos = new FileOutputStream(outFile); - byte buf[] = new byte[4096*1024]; + // wtf this was 4096*1024 which is really excessive for a roughly 4KB file + byte buf[] = new byte[4096]; while (true) { int read = libStream.read(buf); if (read < 0) break; diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index 3f44146b4..56f774071 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -1,5 +1,6 @@ package net.i2p; +import java.io.File; import java.util.HashSet; import java.util.Properties; import java.util.Set; @@ -20,10 +21,12 @@ import net.i2p.crypto.KeyGenerator; import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SessionKeyManager; import net.i2p.crypto.TransientSessionKeyManager; + import net.i2p.data.DataHelper; import net.i2p.data.RoutingKeyGenerator; import net.i2p.stat.StatManager; import net.i2p.util.Clock; import net.i2p.util.ConcurrentHashSet; +import net.i2p.util.FileUtil; import net.i2p.util.FortunaRandomSource; import net.i2p.util.KeyRing; import net.i2p.util.LogManager; @@ -96,6 +99,13 @@ public class I2PAppContext { private volatile boolean _keyGeneratorInitialized; protected volatile boolean _keyRingInitialized; // used in RouterContext private Set _shutdownTasks; + private File _baseDir; + private File _configDir; + private File _routerDir; + private File _pidDir; + private File _logDir; + private File _appDir; + private File _tmpDir; /** @@ -155,8 +165,148 @@ public class I2PAppContext { _logManagerInitialized = false; _keyRingInitialized = false; _shutdownTasks = new ConcurrentHashSet(0); + initializeDirs(); } + /** + * Directories. These are all set at instantiation and will not be changed by + * subsequent property changes. + * All properties, if set, should be absolute paths. + * + * Name Property Method Files + * ----- -------- ----- ----- + * Base i2p.dir.base getBaseDir() lib/, webapps/, docs/, geoip/, licenses/, ... + * Temp i2p.dir.temp getTempDir() Temporary files + * Config i2p.dir.config getConfigDir() *.config, hosts.txt, addressbook/, ... + * + * (the following all default to the same as Config) + * + * Router i2p.dir.router getRouterDir() netDb/, peerProfiles/, router.*, keyBackup/, ... + * Log i2p.dir.log getLogDir() wrapper.log*, logs/ + * PID i2p.dir.pid getPIDDir() wrapper *.pid files, router.ping + * App i2p.dir.app getAppDir() eepsite/, ... + * + * Note that we can't control where the wrapper puts its files. + * + * The app dir is where all data files should be. Apps should always read and write files here, + * using a constructor such as: + * + * String path = mypath; + * File f = new File(path); + * if (!f.isAbsolute()) + * f = new File(_context.geAppDir(), path); + * + * and never attempt to access files in the CWD using + * + * File f = new File("foo"); + * + * An app should assume the CWD is not writable. + * + * Here in I2PAppContext, all the dirs default to CWD. + * However these will be different in RouterContext, as Router.java will set + * the properties in the RouterContext constructor. + * + * Apps should never need to access the base dir, which is the location of the base I2P install. + * However this is provided for the router's use, and for backward compatibility should an app + * need to look there as well. + * + * All dirs except the base are created if they don't exist, but the creation will fail silently. + */ + private void initializeDirs() { + String s = getProperty("i2p.dir.base", System.getProperty("user.dir")); + _baseDir = new File(s); + // config defaults to base + s = getProperty("i2p.dir.config"); + if (s != null) { + _configDir = new File(s); + if (!_configDir.exists()) + _configDir.mkdir(); + } else { + _configDir = _baseDir; + } + _configDir = new File(s); + // router defaults to config + s = getProperty("i2p.dir.router"); + if (s != null) { + _routerDir = new File(s); + if (!_routerDir.exists()) + _routerDir.mkdir(); + } else { + _routerDir = _configDir; + } + // these all default to router + s = getProperty("i2p.dir.pid"); + if (s != null) { + _pidDir = new File(s); + if (!_pidDir.exists()) + _pidDir.mkdir(); + } else { + _pidDir = _routerDir; + } + s = getProperty("i2p.dir.log"); + if (s != null) { + _logDir = new File(s); + if (!_logDir.exists()) + _logDir.mkdir(); + } else { + _logDir = _routerDir; + } + s = getProperty("i2p.dir.app"); + if (s != null) { + _appDir = new File(s); + if (!_appDir.exists()) + _appDir.mkdir(); + } else { + _appDir = _routerDir; + } + // comment these out later, don't want user names in the wrapper logs + System.err.println("Base directory: " + _baseDir.getAbsolutePath()); + System.err.println("Config directory: " + _configDir.getAbsolutePath()); + System.err.println("Router directory: " + _routerDir.getAbsolutePath()); + System.err.println("App directory: " + _appDir.getAbsolutePath()); + System.err.println("Log directory: " + _logDir.getAbsolutePath()); + System.err.println("PID directory: " + _pidDir.getAbsolutePath()); + System.err.println("Temp directory: " + getTempDir().getAbsolutePath()); + } + + public File getBaseDir() { return _baseDir; } + public File getConfigDir() { return _configDir; } + public File getRouterDir() { return _routerDir; } + public File getPIDDir() { return _pidDir; } + public File getLogDir() { return _logDir; } + public File getAppDir() { return _appDir; } + public File getTempDir() { + // fixme don't synchronize every time + synchronized (this) { + if (_tmpDir == null) { + String d = getProperty("i2p.dir.temp", System.getProperty("java.io.tmpdir")); + // our random() probably isn't warmed up yet + String f = "i2p-" + (new java.util.Random()).nextInt() + ".tmp"; + _tmpDir = new File(d, f); + if (_tmpDir.exists()) { + // good or bad ? + } else if (_tmpDir.mkdir()) { + _tmpDir.deleteOnExit(); + } else { + System.err.println("Could not create temp dir " + _tmpDir.getAbsolutePath()); + _tmpDir = new File(_routerDir, "tmp"); + _tmpDir.mkdir(); + } + } + } + return _tmpDir; + } + + /** don't rely on deleteOnExit() */ + public void deleteTempDir() { + synchronized (this) { + if (_tmpDir != null) { + FileUtil.rmdir(_tmpDir, false); + _tmpDir = null; + } + } + } + /** * Access the configuration attributes of this context, using properties * provided during the context construction, or falling back on diff --git a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java index 054bd9d8f..f89d56d09 100644 --- a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java +++ b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java @@ -89,7 +89,7 @@ public class HostsTxtNamingService extends NamingService { String hostsfile = (String)filenames.get(i); Properties hosts = new Properties(); try { - File f = new File(hostsfile); + File f = new File(_context.getRouterDir(), hostsfile); if ( (f.exists()) && (f.canRead()) ) { DataHelper.loadProps(hosts, f, true); @@ -119,7 +119,7 @@ public class HostsTxtNamingService extends NamingService { String hostsfile = (String)filenames.get(i); Properties hosts = new Properties(); try { - File f = new File(hostsfile); + File f = new File(_context.getRouterDir(), hostsfile); if ( (f.exists()) && (f.canRead()) ) { DataHelper.loadProps(hosts, f, true); Set keyset = hosts.keySet(); @@ -145,7 +145,7 @@ public class HostsTxtNamingService extends NamingService { String hostsfile = (String)filenames.get(i); Properties hosts = new Properties(); try { - File f = new File(hostsfile); + File f = new File(_context.getRouterDir(), hostsfile); if ( (f.exists()) && (f.canRead()) ) { DataHelper.loadProps(hosts, f, true); Set keyset = hosts.keySet(); diff --git a/core/java/src/net/i2p/crypto/TrustedUpdate.java b/core/java/src/net/i2p/crypto/TrustedUpdate.java index 06e37544b..6130fa78d 100644 --- a/core/java/src/net/i2p/crypto/TrustedUpdate.java +++ b/core/java/src/net/i2p/crypto/TrustedUpdate.java @@ -1,6 +1,7 @@ package net.i2p.crypto; import java.io.ByteArrayInputStream; +import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -276,7 +277,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= } private static final void showVersionCLI(String signedFile) { - String versionString = new TrustedUpdate().getVersionString(signedFile); + String versionString = new TrustedUpdate().getVersionString(new File(signedFile)); if (versionString == "") System.out.println("No version string found in file '" + signedFile + "'"); @@ -294,7 +295,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= } private static final void verifySigCLI(String signedFile) { - boolean isValidSignature = new TrustedUpdate().verify(signedFile); + boolean isValidSignature = new TrustedUpdate().verify(new File(signedFile)); if (isValidSignature) System.out.println("Signature VALID"); @@ -303,7 +304,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= } private static final void verifyUpdateCLI(String signedFile) { - boolean isUpdate = new TrustedUpdate().isUpdatedVersion(CoreVersion.VERSION, signedFile); + boolean isUpdate = new TrustedUpdate().isUpdatedVersion(CoreVersion.VERSION, new File(signedFile)); if (isUpdate) System.out.println("File version is newer than current version."); @@ -347,7 +348,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= * @return The version string read, or an empty string if no version string * is present. */ - public String getVersionString(String signedFile) { + public String getVersionString(File signedFile) { FileInputStream fileInputStream = null; try { @@ -396,9 +397,9 @@ D8usM7Dxp5yrDrCYZ5AIijc= * @return true if the signed update file's version is newer * than the current version, otherwise false. */ - public boolean isUpdatedVersion(String currentVersion, String signedFile) { + public boolean isUpdatedVersion(String currentVersion, File signedFile) { _newVersion = getVersionString(signedFile); - return needsUpdate(currentVersion, getVersionString(signedFile)); + return needsUpdate(currentVersion, _newVersion); } /** @@ -413,7 +414,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= * @return null if the signature and version were valid and the * data was moved, and an error String otherwise. */ - public String migrateVerified(String currentVersion, String signedFile, String outputFile) { + public String migrateVerified(String currentVersion, File signedFile, File outputFile) { if (!isUpdatedVersion(currentVersion, signedFile)) return "Downloaded version is not greater than current version"; @@ -606,7 +607,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= * @return true if the file has a valid signature, otherwise * false. */ - public boolean verify(String signedFile) { + public boolean verify(File signedFile) { for (int i = 0; i < _trustedKeys.size(); i++) { SigningPublicKey signingPublicKey = new SigningPublicKey(); @@ -662,7 +663,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= } } - return verify(signedFile, signingPublicKey); + return verify(new File(signedFile), signingPublicKey); } /** @@ -676,7 +677,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= * @return true if the file has a valid signature, otherwise * false. */ - public boolean verify(String signedFile, SigningPublicKey signingPublicKey) { + public boolean verify(File signedFile, SigningPublicKey signingPublicKey) { FileInputStream fileInputStream = null; try { diff --git a/core/java/src/net/i2p/data/PrivateKeyFile.java b/core/java/src/net/i2p/data/PrivateKeyFile.java index b5d68ee41..497733cc4 100644 --- a/core/java/src/net/i2p/data/PrivateKeyFile.java +++ b/core/java/src/net/i2p/data/PrivateKeyFile.java @@ -109,6 +109,10 @@ public class PrivateKeyFile { this(new File(file), I2PClientFactory.createClient()); } + public PrivateKeyFile(File file) { + this(file, I2PClientFactory.createClient()); + } + public PrivateKeyFile(File file, I2PClient client) { this.file = file; this.client = client; diff --git a/core/java/src/net/i2p/stat/BufferedStatLog.java b/core/java/src/net/i2p/stat/BufferedStatLog.java index 7fed2d090..ca016622f 100644 --- a/core/java/src/net/i2p/stat/BufferedStatLog.java +++ b/core/java/src/net/i2p/stat/BufferedStatLog.java @@ -1,6 +1,7 @@ package net.i2p.stat; import java.io.BufferedWriter; +import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; @@ -109,6 +110,9 @@ public class BufferedStatLog implements StatLog { String filename = _context.getProperty(StatManager.PROP_STAT_FILE); if (filename == null) filename = StatManager.DEFAULT_STAT_FILE; + File foo = new File(filename); + if (!foo.isAbsolute()) + filename = (new File(_context.getRouterDir(), filename)).getAbsolutePath(); if ( (_outFile != null) && (_outFile.equals(filename)) ) { // noop } else { diff --git a/core/java/src/net/i2p/util/FileUtil.java b/core/java/src/net/i2p/util/FileUtil.java index 677c50086..05346ccfe 100644 --- a/core/java/src/net/i2p/util/FileUtil.java +++ b/core/java/src/net/i2p/util/FileUtil.java @@ -18,6 +18,12 @@ import java.util.zip.ZipFile; /** * General helper methods for messing with files * + * These are static methods that do NOT convert arguments + * to absolute paths for a particular context and directtory. + * + * Callers should ALWAYS provide absolute paths as arguments, + * and should NEVER assume files are in the current working directory. + * */ public class FileUtil { /** diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index 1f957515c..c07445d9b 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -67,8 +67,8 @@ public class LogManager { /** when was the config file last read (or -1 if never) */ private long _configLastRead; - /** filename of the config file */ - private String _location; + /** the config file */ + private File _locationFile; /** Ordered list of LogRecord elements that have not been written out yet */ private List _records; /** List of explicit overrides of log levels (LogLimit objects) */ @@ -115,11 +115,11 @@ public class LogManager { _logs = new HashMap(128); _defaultLimit = Log.ERROR; _configLastRead = 0; - _location = context.getProperty(CONFIG_LOCATION_PROP, CONFIG_LOCATION_DEFAULT); _context = context; _log = getLog(LogManager.class); + String location = context.getProperty(CONFIG_LOCATION_PROP, CONFIG_LOCATION_DEFAULT); + setConfig(location); _consoleBuffer = new LogConsoleBuffer(context); - loadConfig(); _writer = new LogWriter(this); Thread t = new I2PThread(_writer); t.setName("LogWriter"); @@ -196,8 +196,9 @@ public class LogManager { } public void setConfig(String filename) { - _log.debug("Config filename set to " + filename); - _location = filename; + _locationFile = new File(filename); + if (!_locationFile.isAbsolute()) + _locationFile = new File(_context.getConfigDir(), filename); loadConfig(); } @@ -232,20 +233,12 @@ public class LogManager { loadConfig(); } - /// - /// - - // - // - // - private void loadConfig() { - File cfgFile = new File(_location); + File cfgFile = _locationFile; if (!cfgFile.exists()) { if (!_alreadyNoticedMissingConfig) { if (_log.shouldLog(Log.WARN)) - _log.warn("Log file " + _location + " does not exist"); - //System.err.println("Log file " + _location + " does not exist"); + _log.warn("Log file " + _locationFile.getAbsolutePath() + " does not exist"); _alreadyNoticedMissingConfig = true; } parseConfig(new Properties()); @@ -262,9 +255,6 @@ public class LogManager { return; } - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Loading config from " + _location); - Properties p = new Properties(); FileInputStream fis = null; try { @@ -272,7 +262,7 @@ public class LogManager { p.load(fis); _configLastRead = _context.clock().now(); } catch (IOException ioe) { - System.err.println("Error loading logger config from " + new File(_location).getAbsolutePath()); + System.err.println("Error loading logger config from " + cfgFile.getAbsolutePath()); } finally { if (fis != null) try { fis.close(); @@ -540,7 +530,7 @@ public class LogManager { String config = createConfig(); FileOutputStream fos = null; try { - fos = new FileOutputStream(_location); + fos = new FileOutputStream(_locationFile); fos.write(config.getBytes()); return true; } catch (IOException ioe) { diff --git a/core/java/src/net/i2p/util/LogWriter.java b/core/java/src/net/i2p/util/LogWriter.java index 29fcff7cc..c9f2cb756 100644 --- a/core/java/src/net/i2p/util/LogWriter.java +++ b/core/java/src/net/i2p/util/LogWriter.java @@ -15,6 +15,8 @@ import java.io.IOException; import java.io.OutputStream; import java.util.List; +import net.i2p.I2PAppContext; + /** * Log writer thread that pulls log records from the LogManager, writes them to * the current logfile, and rotates the logs as necessary. This also periodically @@ -22,6 +24,7 @@ import java.util.List; * */ class LogWriter implements Runnable { + /** every 10 seconds? why? Just have the gui force a reread after a change?? */ private final static long CONFIG_READ_ITERVAL = 10 * 1000; private long _lastReadConfig = 0; private long _numBytesInCurrentFile = 0; @@ -38,6 +41,7 @@ class LogWriter implements Runnable { public LogWriter(LogManager manager) { _manager = manager; + _lastReadConfig = Clock.getInstance().now(); } public void stopWriting() { @@ -168,15 +172,21 @@ class LogWriter implements Runnable { * */ private File getNextFile(String pattern) { - File f = null; + File f = new File(pattern); + File base = null; + if (!f.isAbsolute()) + base = I2PAppContext.getGlobalContext().getLogDir(); if ( (pattern.indexOf('#') < 0) && (pattern.indexOf('@') <= 0) ) { - return new File(pattern); + if (base != null) + return new File(base, pattern); + else + return f; } int max = _manager.getRotationLimit(); if (_rotationNum == -1) { - return getFirstFile(pattern, max); + return getFirstFile(base, pattern, max); } // we're in rotation, just go to the next @@ -190,9 +200,13 @@ class LogWriter implements Runnable { * Retrieve the first file, updating the rotation number accordingly * */ - private File getFirstFile(String pattern, int max) { + private File getFirstFile(File base, String pattern, int max) { for (int i = 0; i < max; i++) { - File f = new File(replace(pattern, i)); + File f; + if (base != null) + f = new File(base, replace(pattern, i)); + else + f = new File(replace(pattern, i)); if (!f.exists()) { _rotationNum = i; return f; @@ -202,7 +216,11 @@ class LogWriter implements Runnable { // all exist, pick the oldest to replace File oldest = null; for (int i = 0; i < max; i++) { - File f = new File(replace(pattern, i)); + File f; + if (base != null) + f = new File(base, replace(pattern, i)); + else + f = new File(replace(pattern, i)); if (oldest == null) { oldest = f; } else { diff --git a/core/java/src/net/i2p/util/NativeBigInteger.java b/core/java/src/net/i2p/util/NativeBigInteger.java index a6d7e9792..bafaa5e5c 100644 --- a/core/java/src/net/i2p/util/NativeBigInteger.java +++ b/core/java/src/net/i2p/util/NativeBigInteger.java @@ -540,9 +540,10 @@ public class NativeBigInteger extends BigInteger { FileOutputStream fos = null; try { InputStream libStream = resource.openStream(); - outFile = new File(_libPrefix + "jbigi" + _libSuffix); + outFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), _libPrefix + "jbigi" + _libSuffix); fos = new FileOutputStream(outFile); - byte buf[] = new byte[4096*1024]; + // wtf this was 4096*1024 which is really excessive for a roughly 50KB file + byte buf[] = new byte[4096]; while (true) { int read = libStream.read(buf); if (read < 0) break; diff --git a/core/java/src/net/i2p/util/RandomSource.java b/core/java/src/net/i2p/util/RandomSource.java index 550b37b8d..c6ac80a65 100644 --- a/core/java/src/net/i2p/util/RandomSource.java +++ b/core/java/src/net/i2p/util/RandomSource.java @@ -142,7 +142,7 @@ public class RandomSource extends SecureRandom implements EntropyHarvester { private static final String SEEDFILE = "prngseed.rnd"; public static final void writeSeed(byte buf[]) { - File f = new File(SEEDFILE); + File f = new File(I2PAppContext.getGlobalContext().getConfigDir(), SEEDFILE); FileOutputStream fos = null; try { fos = new FileOutputStream(f); @@ -164,7 +164,7 @@ public class RandomSource extends SecureRandom implements EntropyHarvester { } private static final boolean seedFromFile(String filename, byte buf[]) { - File f = new File(filename); + File f = new File(I2PAppContext.getGlobalContext().getConfigDir(), filename); if (f.exists()) { FileInputStream fis = null; try { diff --git a/core/java/src/net/i2p/util/WorkingDir.java b/core/java/src/net/i2p/util/WorkingDir.java new file mode 100644 index 000000000..4e47f6458 --- /dev/null +++ b/core/java/src/net/i2p/util/WorkingDir.java @@ -0,0 +1,481 @@ +package net.i2p.util; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Properties; + +import net.i2p.data.DataHelper; + +/** + * Get a working directory for i2p. + * + * For the location, first try the system property i2p.dir.config + * Next try $HOME/.i2p on linux or %APPDATA%\I2P on Windows. + * + * If the dir exists, return it. + * Otherwise, attempt to create it, and copy files from the base directory. + * To successfully copy, the base install dir must be the system property i2p.dir.base + * or else must be in $CWD. + * + * If I2P was run from the install directory in the past, + * and migrateOldData = true, copy the + * necessary data files (except i2psnark/) over to the new working directory. + * + * Otherwise, just copy over a limited number of files over. + * + * Do not ever copy or move the old i2psnark/ directory, as if the + * old and new locations are on different file systems, this could + * be quite slow. + * + * Modify some files while copying, see methods below. + * + * After migration, the router will run using the new directory. + * The wrapper, however, must be stopped and restarted from the new script - until then, + * it will continue to write to wrapper.log* in the old directory. + * + * @param whether to copy all data over from an existing install + */ +public class WorkingDir { + + private final static String PROP_BASE_DIR = "i2p.dir.base"; + private final static String PROP_WORKING_DIR = "i2p.dir.config"; + private final static String WORKING_DIR_DEFAULT_WINDOWS = "I2P"; + private final static String WORKING_DIR_DEFAULT = ".i2p"; + + /** + * Only call this once on router invocation. + * Caller should store the return value for future reference. + */ + public static String getWorkingDir(boolean migrateOldData) { + String dir = System.getProperty(PROP_WORKING_DIR); + boolean isWindows = System.getProperty("os.name").startsWith("Win"); + File dirf = null; + if (dir != null) { + dirf = new File(dir); + } else { + String home = System.getProperty("user.home"); + if (isWindows) { + String appdata = System.getenv("APPDATA"); + if (appdata != null) + home = appdata; + dirf = new File(home, WORKING_DIR_DEFAULT_WINDOWS); + } else { + dirf = new File(home, WORKING_DIR_DEFAULT); + } + } + // where we are now + String cwd = System.getProperty(PROP_BASE_DIR); + if (cwd == null) + cwd = System.getProperty("user.dir"); + // where we want to go + String rv = dirf.getAbsolutePath(); + if (dirf.exists()) { + if (dirf.isDirectory()) + return rv; // all is good, we found the user directory + System.err.println("Wanted to use " + rv + " for a working directory but it is not a directory"); + return cwd; + } + if (!dirf.mkdir()) { + System.err.println("Wanted to use " + rv + " for a working directory but could not create it"); + return cwd; + } + + // Check for a hosts.txt file, if it exists then I2P is there + File oldDirf = new File(cwd); + File test = new File(oldDirf, "hosts.txt"); + if (!test.exists()) { + System.err.println("ERROR - Cannot find I2P installation in " + cwd); + return cwd; + } + + // Check for a router.keys file, if it exists it's an old install, + // and only migrate the data files if told to do so + test = new File(oldDirf, "router.keys"); + boolean oldInstall = test.exists(); + migrateOldData &= oldInstall; + + // Do the copying + if (migrateOldData) + System.err.println("Migrating data files to new user directory " + rv); + else + System.err.println("Setting up new user directory " + rv); + boolean success = migrate(MIGRATE_BASE, oldDirf, dirf); + // this one must be after MIGRATE_BASE + success &= migrateJettyXml(oldDirf, dirf); + success &= migrateWrapperConfig(oldDirf, dirf); + if (migrateOldData) { + success &= migrate(MIGRATE_DATA, oldDirf, dirf); + success &= migrateI2PTunnelKeys(oldDirf, dirf); + success &= migrateSnark(oldDirf, dirf); + // new installs will have updated scripts left in the install dir + // don't bother migrating runplain.sh or i2prouter.bat + if (!isWindows) + success &= migrateI2prouter(oldDirf, dirf); + } else if (!oldInstall) { + // copy the default i2psnark.config over + success &= migrate("i2psnark.config", oldDirf, dirf); + } + + // Report success or failure + if (success) { + System.err.println("Successfully copied data files to new user directory " + rv); + if (migrateOldData) { + System.err.println("Libraries and other files remain in the old directory " + cwd + ", do not remove them."); + System.err.println("You should manually move any non-standard files, such as additional eepsite directories and key files"); + System.err.println("After verifying that all is working, you may delete the following data files and directories in " + + cwd + ": " + MIGRATE_DATA.replace(',', ' ') + " i2psnark.config tmp work"); + if (System.getProperty("wrapper.version") != null) + System.err.println("Note that until you shutdown your router completely and restart, the wrapper will continue" + + " to log to the old wrapper logs in " + cwd); + if (!isWindows) + System.err.println("From now on, you should now use the i2prouter" + + " script in the " + rv + " directory to start i2p." + + " You may copy or move this script elsewhere, you need not run it from that directory."); + } + return rv; + } else { + System.err.println("FAILED copy of some or all data files to new directory " + rv); + System.err.println("Check logs for details"); + System.err.println("Continung to use data files in old directory " + cwd); + return cwd; + } + } + + /** + * files and directories from the base install to copy over + * None of these should be included in i2pupdate.zip + * + * The user should not delete these in the old location, leave them as templates for new users + */ + private static final String MIGRATE_BASE = + // base install - dirs + // We don't currently have a default addressbook/ in the base distribution, + // but distros might put one in + "addressbook,eepsite," + + // base install - files + // We don't currently have a default router.config or logger.config in the base distribution, + // but distros might put one in + "blocklist.txt,clients.config,hosts.txt,i2ptunnel.config,jetty-i2psnark.xml," + + "logger.config,router.config,systray.config"; + + /** + * files and directories from an old single-directory installation to copy over - NOT including snark + * None of these should be included in i2pupdate.zip + * + * The user can be advised to delete these from the old location + */ + private static final String MIGRATE_DATA = + // post install - dirs + // not required to copy - tmp/, work/ + // addressbook included in MIGRATE_BASE above + "keyBackup,logs,netDb,peerProfiles," + + // post install - files + // not required to copy - prngseed.rnd + // logger.config and router.config included in MIGRATE_BASE above + "bob.config,privatehosts.txt,router.info,router.keys," + + "sam.keys,susimail.config,userhosts.txt,webapps.config," + + "wrapper.log,wrapper.log.1,wrapper.log.2"; + + private static boolean migrate(String list, File olddir, File todir) { + boolean rv = true; + String files[] = list.split(","); + for (int i = 0; i < files.length; i++) { + File from = new File(olddir, files[i]); + if (!copy(from, todir)) { + System.err.println("Error copying " + from.getAbsolutePath()); + rv = false; + } + } + return rv; + } + + /** + * Copy over the i2psnark.config file with modifications + */ + private static boolean migrateSnark(File olddir, File todir) { + boolean rv = true; + File oldSnark = new File(olddir, "i2psnark"); + File newSnark = new File(todir, "i2psnark"); + File oldSnarkConfig = new File(olddir, "i2psnark.config"); + File newSnarkConfig = new File(todir, "i2psnark.config"); + boolean hasData = oldSnark.exists(); + if (hasData) { + File children[] = oldSnark.listFiles(); + hasData = children != null && children.length > 0; + } + if (oldSnarkConfig.exists()) { + if (hasData) { + // edit the snark config file to point to the old location, we aren't moving the data + try { + Properties props = new Properties(); + DataHelper.loadProps(props, oldSnarkConfig); + String dir = props.getProperty("i2psnark.dir"); + if (dir == null) + dir = "i2psnark"; + // change relative to absolute path + File f = new File(dir); + props.setProperty("i2psnark.dir", f.getAbsolutePath()); + DataHelper.storeProps(props, newSnarkConfig); + System.err.println("Copied i2psnark.config with modifications"); + } catch (IOException ioe) { + System.err.println("FAILED copy i2psnark.config"); + rv = false; + } + } else { + // copy the i2psnark config file over + copy(newSnarkConfig, todir); + System.err.println("Copied i2psnark.config"); + } + } else { + if (hasData) { + // data but no previous config file (unlikely) - make new config file + try { + Properties props = new Properties(); + File f = new File("i2psnark"); + props.setProperty("i2psnark.dir", f.getAbsolutePath()); + DataHelper.storeProps(props, newSnarkConfig); + } catch (IOException ioe) { + // ignore + } + } // else no config and no data + } + if (hasData) { + /************* + // crude attempt to detect same filesystem + if ((oldSnark.getAbsolutePath().startsWith("/home/") && newSnark.getAbsolutePath().startsWith("/home/")) || + (System.getProperty("os.name").toLowerCase.indexOf("windows") >= 0 && + oldSnark.getAbsolutePath().substring(0,1).equals(newSnark.getAbsolutePath().substring(0,1) && + oldSnark.getAbsolutePath().substring(1,2).equals(":\\") && + newSnark.getAbsolutePath().substring(1,2).equals(":\\"))) { + // OK, apparently in same file system + // move everything + } + **************/ + System.err.println("NOT moving the i2psnark data directory " + oldSnark.getAbsolutePath() + + " to the new directory " + newSnark.getAbsolutePath() + + ". You may move the directory contents manually WHILE I2P IS NOT RUNNING," + + " and edit the file " + newSnarkConfig.getAbsolutePath() + + " to configure i2psnark to use a different location by editing the i2psnark.dir configuration to be" + + " i2psnark.dir=" + oldSnark.getAbsolutePath() + + " and restart, or you may leave the i2psnark directory in its old location."); + } + return true; + } + + /** + * Copy over the i2prouter file with modifications + * The resulting script can be run from any location. + */ + private static boolean migrateI2prouter(File olddir, File todir) { + File oldFile = new File(olddir, "i2prouter"); + File newFile = new File(todir, "i2prouter"); + FileInputStream in = null; + PrintWriter out = null; + try { + in = new FileInputStream(oldFile); + out = new PrintWriter(new BufferedWriter(new FileWriter(newFile))); + boolean firstTime = true; + String s = null; + while ((s = DataHelper.readLine(in)) != null) { + if (s.equals("WRAPPER_CMD=\"./i2psvc\"")) { + // i2psvc in the old location + File f = new File("i2psvc"); + s = "WRAPPER_CMD=\"" + f.getAbsolutePath() + "\""; + } else if(s.equals("WRAPPER_CONF=\"wrapper.config\"")) { + // wrapper.config the new location + File f = new File(todir, "wrapper.config"); + s = "WRAPPER_CONF=\"" + f.getAbsolutePath() + "\""; + } else if(s.equals("PIDDIR=\".\"")) { + // i2p.pid in the new location + s = "PIDDIR=\"" + todir.getAbsolutePath() + "\""; + } + out.println(s); + if (firstTime) { + // first line was #!/bin/sh, so had to wait until now + out.println("# Modified by I2P User dir migration script"); + firstTime = false; + } + } + System.err.println("Copied i2prouter with modifications"); + return true; + } catch (IOException ioe) { + if (in != null) { + System.err.println("FAILED copy i2prouter"); + return false; + } + return true; + } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) out.close(); + } + } + + /** + * Copy over the wrapper.config file with modifications + */ + private static boolean migrateWrapperConfig(File olddir, File todir) { + File oldFile = new File(olddir, "wrapper.config"); + File newFile = new File(todir, "wrapper.config"); + FileInputStream in = null; + PrintWriter out = null; + try { + in = new FileInputStream(oldFile); + out = new PrintWriter(new BufferedWriter(new FileWriter(newFile))); + out.println("# Modified by I2P User dir migration script"); + String s = null; + // Don't use replaceFirst because backslashes in the replacement string leads to havoc + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4689750 + // "Note that backslashes and dollar signs in the replacement string may cause the results + // to be different than if it were being treated as a literal replacement string. + // Dollar signs may be treated as references to captured subsequences as described above, + // and backslashes are used to escape literal characters in the replacement string." + while ((s = DataHelper.readLine(in)) != null) { + if (s.startsWith("wrapper.java.classpath.")) { + // libraries in the old location + s = s.replace("=lib/", '=' + olddir.getAbsolutePath() + File.separatorChar + "lib" + File.separatorChar); + } else if (s.startsWith("wrapper.java.library.path.")) { + // libraries in the old location + if (s.contains("=.")) + s = s.replace("=.", '=' + olddir.getAbsolutePath()); + else if (s.contains("=lib")) + s = s.replace("=lib", '=' + olddir.getAbsolutePath() + File.separatorChar + "lib"); + } else if (s.startsWith("wrapper.logfile=wrapper.log")) { + // wrapper logs in the new location + s = s.replace("=", '=' + todir.getAbsolutePath() + File.separatorChar); + } else if (s.startsWith("wrapper.pidfile=i2p.pid")) { + // i2p.pid in the new location + s = s.replace("=", '=' + todir.getAbsolutePath() + File.separatorChar); + } + out.println(s); + } + System.err.println("Copied wrapper.config with modifications"); + return true; + } catch (IOException ioe) { + if (in != null) { + System.err.println("FAILED copy wrapper.config"); + return false; + } + return false; + } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) out.close(); + } + } + + /** + * Copy over the jetty.xml file with modifications + * It was already copied over once in migrate(), throw that out and + * do it again with modifications. + */ + private static boolean migrateJettyXml(File olddir, File todir) { + File eepsite1 = new File(olddir, "eepsite"); + File oldFile = new File(eepsite1, "jetty.xml"); + File eepsite2 = new File(todir, "eepsite"); + File newFile = new File(eepsite2, "jetty.xml"); + FileInputStream in = null; + PrintWriter out = null; + try { + in = new FileInputStream(oldFile); + out = new PrintWriter(new BufferedWriter(new FileWriter(newFile))); + String s = null; + while ((s = DataHelper.readLine(in)) != null) { + if (s.indexOf("./eepsite/") >= 0) { + s = s.replace("./eepsite/", todir.getAbsolutePath() + File.separatorChar + "eepsite" + File.separatorChar); + } + out.println(s); + } + out.println(""); + System.err.println("Copied jetty.xml with modifications"); + return true; + } catch (IOException ioe) { + if (in != null) { + System.err.println("FAILED copy jetty.xml"); + return false; + } + return false; + } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) out.close(); + } + } + + /** + * Relatively recent default i2ptunnel key file name + */ + private static boolean migrateI2PTunnelKeys(File olddir, File todir) { + for (int i = 0; i < 100; i++) { + copy(new File(olddir, "i2ptunnel" + i + "-privKeys.dat"), todir); + } + return true; + } + + /** + * Recursive copy a file or dir to a dir + * + * @param src file or directory, need not exist + * @param target the directory to copy to, will be created if it doesn't exist + * @return true for success OR if src does not exist + */ + public static final boolean copy(File src, File targetDir) { + if (!src.exists()) + return true; + if (!targetDir.exists()) { + if (!targetDir.mkdir()) { + System.err.println("FAILED copy " + src.getPath()); + return false; + } + } + File targetFile = new File(targetDir, src.getName()); + if (!src.isDirectory()) + return copyFile(src, targetFile); + File children[] = src.listFiles(); + if (children == null) { + System.err.println("FAILED copy " + src.getPath()); + return false; + } + boolean rv = true; + for (int i = 0; i < children.length; i++) { + rv &= copy(children[i], targetFile); + } + return rv; + } + + /** + * @param src not a directory, must exist + * @param dest not a directory, will be overwritten if existing + * @@reurn true if it was copied successfully + */ + public static boolean copyFile(File src, File dst) { + if (!src.exists()) return false; + boolean rv = true; + + byte buf[] = new byte[4096]; + FileInputStream in = null; + FileOutputStream out = null; + try { + in = new FileInputStream(src); + out = new FileOutputStream(dst); + + int read = 0; + while ( (read = in.read(buf)) != -1) + out.write(buf, 0, read); + + System.err.println("Copied " + src.getPath()); + } catch (IOException ioe) { + System.err.println("FAILED copy " + src.getPath()); + rv = false; + } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) try { out.close(); } catch (IOException ioe) {} + } + if (rv) + dst.setLastModified(src.lastModified()); + return rv; + } +} diff --git a/router/java/src/net/i2p/router/Blocklist.java b/router/java/src/net/i2p/router/Blocklist.java index 7546ac4a7..6195d6a76 100644 --- a/router/java/src/net/i2p/router/Blocklist.java +++ b/router/java/src/net/i2p/router/Blocklist.java @@ -166,6 +166,8 @@ public class Blocklist { */ private void readBlocklistFile(String file) { File BLFile = new File(file); + if (!BLFile.isAbsolute()) + BLFile = new File(_context.getConfigDir(), file); if (BLFile == null || (!BLFile.exists()) || BLFile.length() <= 0) { if (_log.shouldLog(Log.WARN)) _log.warn("Blocklist file not found: " + file); @@ -701,6 +703,8 @@ public class Blocklist { private synchronized void shitlistForever(Hash peer) { String file = _context.getProperty(PROP_BLOCKLIST_FILE, BLOCKLIST_FILE_DEFAULT); File BLFile = new File(file); + if (!BLFile.isAbsolute()) + BLFile = new File(_context.getConfigDir(), file); if (BLFile == null || (!BLFile.exists()) || BLFile.length() <= 0) { if (_log.shouldLog(Log.ERROR)) _log.error("Blocklist file not found: " + file); diff --git a/router/java/src/net/i2p/router/KeyManager.java b/router/java/src/net/i2p/router/KeyManager.java index 9fc62a70f..711759a40 100644 --- a/router/java/src/net/i2p/router/KeyManager.java +++ b/router/java/src/net/i2p/router/KeyManager.java @@ -147,10 +147,8 @@ public class KeyManager { super(KeyManager.this._context); } public void runJob() { - String keyDir = getContext().getProperty(PROP_KEYDIR); - if (keyDir == null) - keyDir = DEFAULT_KEYDIR; - File dir = new File(keyDir); + String keyDir = getContext().getProperty(PROP_KEYDIR, DEFAULT_KEYDIR); + File dir = new File(getContext().getRouterDir(), keyDir); if (!dir.exists()) dir.mkdirs(); if (dir.exists() && dir.isDirectory() && dir.canRead() && dir.canWrite()) { diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 1e8e90552..35518feda 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -44,6 +44,7 @@ import net.i2p.util.I2PThread; import net.i2p.util.Log; import net.i2p.util.SimpleScheduler; import net.i2p.util.SimpleTimer; +import net.i2p.util.WorkingDir; /** * Main driver for the router. @@ -53,6 +54,7 @@ public class Router { private Log _log; private RouterContext _context; private final Properties _config; + /** full path */ private String _configFilename; private RouterInfo _routerInfo; private long _started; @@ -104,14 +106,6 @@ public class Router { public Router(Properties envProps) { this(null, envProps); } public Router(String configFilename) { this(configFilename, null); } public Router(String configFilename, Properties envProps) { - if (!beginMarkingLiveliness(envProps)) { - System.err.println("ERROR: There appears to be another router already running!"); - System.err.println(" Please make sure to shut down old instances before starting up"); - System.err.println(" a new one. If you are positive that no other instance is running,"); - System.err.println(" please delete the file " + getPingFile(envProps)); - System.exit(-1); - } - _gracefulExitCode = -1; _config = new Properties(); @@ -125,7 +119,35 @@ public class Router { _configFilename = configFilename; } + // we need the user directory figured out by now, so figure it out here rather than + // in the RouterContext() constructor. + // + // Fixme read config file before migration? or before? or both? + // + // Then add it to envProps (but not _config, we don't want it in the router.config file) + // where it will then be available to all via _context.dir() + // + // This call also migrates all files to the new working directory, + // including router.config + // + + // Do we copy all the data files to the new directory? default false + String migrate = System.getProperty("i2p.dir.migrate"); + boolean migrateFiles = Boolean.valueOf(migrate).booleanValue(); + String userDir = WorkingDir.getWorkingDir(migrateFiles); + + // Use the router.config file specified in the router.configLocation property + // (default "router.config"), + // if it is an abolute path, otherwise look in the userDir returned by getWorkingDir + // replace relative path with absolute + File cf = new File(_configFilename); + if (!cf.isAbsolute()) { + cf = new File(userDir, _configFilename); + _configFilename = cf.getAbsolutePath(); + } + readConfig(); + if (envProps == null) { envProps = _config; } else { @@ -135,11 +157,42 @@ public class Router { envProps.setProperty(k, v); } } + // This doesn't work, guess it has to be in the static block above? // if (Boolean.valueOf(envProps.getProperty("router.disableIPv6")).booleanValue()) // System.setProperty("java.net.preferIPv4Stack", "true"); + if (envProps.getProperty("i2p.dir.config") == null) + envProps.setProperty("i2p.dir.config", userDir); + + // The important thing that happens here is the directory paths are set and created + // i2p.dir.router defaults to i2p.dir.config + // i2p.dir.app defaults to i2p.dir.router + // i2p.dir.log defaults to i2p.dir.router + // i2p.dir.pid defaults to i2p.dir.router + // i2p.dir.base defaults to user.dir == $CWD _context = new RouterContext(this, envProps); + + // This is here so that we can get the directory location from the context + // for the ping file + if (!beginMarkingLiveliness()) { + System.err.println("ERROR: There appears to be another router already running!"); + System.err.println(" Please make sure to shut down old instances before starting up"); + System.err.println(" a new one. If you are positive that no other instance is running,"); + System.err.println(" please delete the file " + getPingFile().getAbsolutePath()); + System.exit(-1); + } + + // This is here so that we can get the directory location from the context + // for the zip file and the base location to unzip to. + // If it does an update, it never returns. + // I guess it's better to have the other-router check above this, we don't want to + // overwrite an existing running router's jar files. Other than ours. + installUpdates(); + + // NOW we start all the activity + _context.initAll(); + _routerInfo = null; _higherVersionSeen = false; _log = _context.logManager().getLog(Router.class); @@ -245,6 +298,7 @@ public class Router { _context.keyManager().startup(); + // why are we reading this again, it's read in the constructor readConfig(); setupHandlers(); @@ -285,6 +339,7 @@ public class Router { } } + /** this does not use ctx.getConfigDir(), must provide a full path in filename */ private static Properties getConfig(RouterContext ctx, String filename) { Log log = null; if (ctx != null) { @@ -456,11 +511,11 @@ public class Router { }; static final String IDENTLOG = "identlog.txt"; - public static void killKeys() { + public void killKeys() { new Exception("Clearing identity files").printStackTrace(); int remCount = 0; for (int i = 0; i < _rebuildFiles.length; i++) { - File f = new File(_rebuildFiles[i]); + File f = new File(_context.getRouterDir(),_rebuildFiles[i]); if (f.exists()) { boolean removed = f.delete(); if (removed) { @@ -474,7 +529,7 @@ public class Router { if (remCount > 0) { FileOutputStream log = null; try { - log = new FileOutputStream(IDENTLOG, true); + log = new FileOutputStream(new File(_context.getRouterDir(), IDENTLOG), true); log.write((new Date() + ": Old router identity keys cleared\n").getBytes()); } catch (IOException ioe) { // ignore @@ -814,8 +869,9 @@ public class Router { try { _context.messageValidator().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the message validator", t); } try { _context.inNetMessagePool().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the inbound net pool", t); } //try { _sessionKeyPersistenceHelper.shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the session key manager", t); } + _context.deleteTempDir(); RouterContext.listContexts().remove(_context); - dumpStats(); + //dumpStats(); finalShutdown(exitCode); } @@ -833,7 +889,7 @@ public class Router { killKeys(); } - File f = new File(getPingFile()); + File f = getPingFile(); f.delete(); if (_killVMOnEnd) { try { Thread.sleep(1000); } catch (InterruptedException ie) {} @@ -1003,8 +1059,9 @@ public class Router { public static void main(String args[]) { System.out.println("Starting I2P " + RouterVersion.FULL_VERSION); - installUpdates(); - verifyWrapperConfig(); + // installUpdates() moved to constructor so we can get file locations from the context + // installUpdates(); + //verifyWrapperConfig(); Router r = new Router(); if ( (args != null) && (args.length == 1) && ("rebuild".equals(args[0])) ) { r.rebuildNewIdentity(); @@ -1015,11 +1072,30 @@ public class Router { public static final String UPDATE_FILE = "i2pupdate.zip"; - private static void installUpdates() { - File updateFile = new File(UPDATE_FILE); - if (updateFile.exists()) { + /** + * Unzip update file found in the router dir OR base dir, to the base dir + * + * If we can't write to the base dir, complain. + */ + private void installUpdates() { + File updateFile = new File(_context.getRouterDir(), UPDATE_FILE); + boolean exists = updateFile.exists(); + if (!exists) { + updateFile = new File(_context.getBaseDir(), UPDATE_FILE); + exists = updateFile.exists(); + } + if (exists) { + File test = new File(_context.getBaseDir(), "history.txt"); + if ((!test.canWrite()) || (!_context.getBaseDir().canWrite())) { + String msg = "ERROR: No write permissions on " + _context.getBaseDir() + + " to extract software update file"; + System.out.println(msg); + _log.log(Log.CRIT, msg); + // carry on + return; + } System.out.println("INFO: Update file exists [" + UPDATE_FILE + "] - installing"); - boolean ok = FileUtil.extractZip(updateFile, new File(".")); + boolean ok = FileUtil.extractZip(updateFile, _context.getBaseDir()); if (ok) System.out.println("INFO: Update installed"); else @@ -1037,6 +1113,7 @@ public class Router { } } +/******* private static void verifyWrapperConfig() { File cfgUpdated = new File("wrapper.config.updated"); if (cfgUpdated.exists()) { @@ -1046,15 +1123,22 @@ public class Router { System.exit(EXIT_HARD); } } +*******/ +/* private static String getPingFile(Properties envProps) { if (envProps != null) return envProps.getProperty("router.pingFile", "router.ping"); else return "router.ping"; } - private String getPingFile() { - return _context.getProperty("router.pingFile", "router.ping"); +*/ + private File getPingFile() { + String s = _context.getProperty("router.pingFile", "router.ping"); + File f = new File(s); + if (!f.isAbsolute()) + f = new File(_context.getPIDDir(), s); + return f; } static final long LIVELINESS_DELAY = 60*1000; @@ -1066,9 +1150,8 @@ public class Router { * * @return true if the router is the only one running */ - private boolean beginMarkingLiveliness(Properties envProps) { - String filename = getPingFile(envProps); - File f = new File(filename); + private boolean beginMarkingLiveliness() { + File f = getPingFile(); if (f.exists()) { long lastWritten = f.lastModified(); if (System.currentTimeMillis()-lastWritten > LIVELINESS_DELAY) { @@ -1376,15 +1459,14 @@ private static class PersistRouterInfoJob extends JobImpl { if (_log.shouldLog(Log.DEBUG)) _log.debug("Persisting updated router info"); - String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME); - if (infoFilename == null) - infoFilename = Router.PROP_INFO_FILENAME_DEFAULT; + String infoFilename = getContext().getProperty(PROP_INFO_FILENAME, PROP_INFO_FILENAME_DEFAULT); + File infoFile = new File(getContext().getRouterDir(), infoFilename); RouterInfo info = getContext().router().getRouterInfo(); FileOutputStream fos = null; try { - fos = new FileOutputStream(infoFilename); + fos = new FileOutputStream(infoFile); info.writeBytes(fos); } catch (DataFormatException dfe) { _log.error("Error rebuilding the router information", dfe); diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java index 719224058..656b1299b 100644 --- a/router/java/src/net/i2p/router/RouterContext.java +++ b/router/java/src/net/i2p/router/RouterContext.java @@ -70,7 +70,11 @@ public class RouterContext extends I2PAppContext { public RouterContext(Router router, Properties envProps) { super(filterProps(envProps)); _router = router; - initAll(); + // Disabled here so that the router can get a context and get the + // directory locations from it, to do an update, without having + // to init everything. Caller MUST call initAll() afterwards. + // Sorry, this breaks some main() unit tests out there. + //initAll(); _contexts.add(this); } /** @@ -86,7 +90,7 @@ public class RouterContext extends I2PAppContext { envProps.setProperty("time.disabled", "false"); return envProps; } - private void initAll() { + public void initAll() { //_adminManager = new AdminManager(this); if ("false".equals(getProperty("i2p.dummyClientFacade", "false"))) _clientManagerFacade = new ClientManagerFacadeImpl(this); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java index 282b67e0c..92124d690 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java @@ -410,7 +410,7 @@ class PersistentDataStore extends TransientDataStore { private File getDbDir() throws IOException { - File f = new File(_dbDir); + File f = new File(_context.getRouterDir(), _dbDir); if (!f.exists()) { boolean created = f.mkdirs(); if (!created) diff --git a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java index 4f5d23611..e1f265d8f 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java +++ b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java @@ -297,18 +297,8 @@ class ProfilePersistenceHelper { private File getProfileDir() { if (_profileDir == null) { - String dir = null; - if (_context.router() == null) { - dir = _context.getProperty(PROP_PEER_PROFILE_DIR, DEFAULT_PEER_PROFILE_DIR); - } else { - dir = _context.router().getConfigSetting(PROP_PEER_PROFILE_DIR); - if (dir == null) { - _log.info("No peer profile dir specified [" + PROP_PEER_PROFILE_DIR - + "], using [" + DEFAULT_PEER_PROFILE_DIR + "]"); - dir = DEFAULT_PEER_PROFILE_DIR; - } - } - _profileDir = new File(dir); + String dir = _context.getProperty(PROP_PEER_PROFILE_DIR, DEFAULT_PEER_PROFILE_DIR); + _profileDir = new File(_context.getRouterDir(), dir); } return _profileDir; } diff --git a/router/java/src/net/i2p/router/startup/ClientAppConfig.java b/router/java/src/net/i2p/router/startup/ClientAppConfig.java index 316c0b500..f5d48cb1f 100644 --- a/router/java/src/net/i2p/router/startup/ClientAppConfig.java +++ b/router/java/src/net/i2p/router/startup/ClientAppConfig.java @@ -43,6 +43,8 @@ public class ClientAppConfig { Properties rv = new Properties(); String clientConfigFile = ctx.getProperty(PROP_CLIENT_CONFIG_FILENAME, DEFAULT_CLIENT_CONFIG_FILENAME); File cfgFile = new File(clientConfigFile); + if (!cfgFile.isAbsolute()) + cfgFile = new File(ctx.getConfigDir(), clientConfigFile); // fall back to use router.config's clientApp.* lines if (!cfgFile.exists()) @@ -91,9 +93,12 @@ public class ClientAppConfig { public static void writeClientAppConfig(RouterContext ctx, List apps) { String clientConfigFile = ctx.getProperty(PROP_CLIENT_CONFIG_FILENAME, DEFAULT_CLIENT_CONFIG_FILENAME); + File cfgFile = new File(clientConfigFile); + if (!cfgFile.isAbsolute()) + cfgFile = new File(ctx.getConfigDir(), clientConfigFile); FileOutputStream fos = null; try { - fos = new FileOutputStream(clientConfigFile); + fos = new FileOutputStream(cfgFile); StringBuffer buf = new StringBuffer(2048); for(int i = 0; i < apps.size(); i++) { ClientAppConfig app = (ClientAppConfig) apps.get(i); diff --git a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java index 92b176c30..979cefa78 100644 --- a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java @@ -8,6 +8,7 @@ package net.i2p.router.startup; * */ +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashSet; @@ -76,16 +77,14 @@ public class CreateRouterInfoJob extends JobImpl { info.sign(signingPrivKey); - String infoFilename = getContext().router().getConfigSetting(Router.PROP_INFO_FILENAME); - if (infoFilename == null) - infoFilename = Router.PROP_INFO_FILENAME_DEFAULT; - fos1 = new FileOutputStream(infoFilename); + String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT); + File ifile = new File(getContext().getRouterDir(), infoFilename); + fos1 = new FileOutputStream(ifile); info.writeBytes(fos1); - String keyFilename = getContext().router().getConfigSetting(Router.PROP_KEYS_FILENAME); - if (keyFilename == null) - keyFilename = Router.PROP_KEYS_FILENAME_DEFAULT; - fos2 = new FileOutputStream(keyFilename); + String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT); + File kfile = new File(getContext().getRouterDir(), keyFilename); + fos2 = new FileOutputStream(kfile); privkey.writeBytes(fos2); signingPrivKey.writeBytes(fos2); pubkey.writeBytes(fos2); @@ -96,7 +95,7 @@ public class CreateRouterInfoJob extends JobImpl { getContext().keyManager().setPrivateKey(privkey); getContext().keyManager().setPublicKey(pubkey); - _log.info("Router info created and stored at " + infoFilename + " with private keys stored at " + keyFilename + " [" + info + "]"); + _log.info("Router info created and stored at " + ifile.getAbsolutePath() + " with private keys stored at " + kfile.getAbsolutePath() + " [" + info + "]"); } catch (DataFormatException dfe) { _log.error("Error building the new router information", dfe); } catch (IOException ioe) { diff --git a/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java b/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java index 0374922af..f3428c447 100644 --- a/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java @@ -51,21 +51,17 @@ public class LoadRouterInfoJob extends JobImpl { } private void loadRouterInfo() { - String routerInfoFile = getContext().router().getConfigSetting(Router.PROP_INFO_FILENAME); - if (routerInfoFile == null) - routerInfoFile = Router.PROP_INFO_FILENAME_DEFAULT; + String routerInfoFile = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT); RouterInfo info = null; boolean failedRead = false; - String keyFilename = getContext().router().getConfigSetting(Router.PROP_KEYS_FILENAME); - if (keyFilename == null) - keyFilename = Router.PROP_KEYS_FILENAME_DEFAULT; + String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT); - File rif = new File(routerInfoFile); + File rif = new File(getContext().getRouterDir(), routerInfoFile); if (rif.exists()) _infoExists = true; - File rkf = new File(keyFilename); + File rkf = new File(getContext().getRouterDir(), keyFilename); if (rkf.exists()) _keysExist = true; @@ -98,14 +94,14 @@ public class LoadRouterInfoJob extends JobImpl { _us = info; } catch (IOException ioe) { - _log.error("Error reading the router info from " + routerInfoFile + " and the keys from " + keyFilename, ioe); + _log.error("Error reading the router info from " + rif.getAbsolutePath() + " and the keys from " + rkf.getAbsolutePath(), ioe); _us = null; rif.delete(); rkf.delete(); _infoExists = false; _keysExist = false; } catch (DataFormatException dfe) { - _log.error("Corrupt router info or keys at " + routerInfoFile + " / " + keyFilename, dfe); + _log.error("Corrupt router info or keys at " + rif.getAbsolutePath() + " / " + rkf.getAbsolutePath(), dfe); _us = null; rif.delete(); rkf.delete(); diff --git a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java index 967bc7a79..fda82de8e 100644 --- a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java @@ -57,18 +57,11 @@ public class RebuildRouterInfoJob extends JobImpl { public void runJob() { _log.debug("Testing to rebuild router info"); - String infoFile = getContext().router().getConfigSetting(Router.PROP_INFO_FILENAME); - if (infoFile == null) { - _log.debug("Info filename not configured, defaulting to " + Router.PROP_INFO_FILENAME_DEFAULT); - infoFile = Router.PROP_INFO_FILENAME_DEFAULT; - } + String infoFile = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT); + File info = new File(getContext().getRouterDir(), infoFile); + String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT); + File keyFile = new File(getContext().getRouterDir(), keyFilename); - String keyFilename = getContext().router().getConfigSetting(Router.PROP_KEYS_FILENAME); - if (keyFilename == null) - keyFilename = Router.PROP_KEYS_FILENAME_DEFAULT; - File keyFile = new File(keyFilename); - - File info = new File(infoFile); if (!info.exists() || !keyFile.exists()) { _log.info("Router info file [" + info.getAbsolutePath() + "] or private key file [" + keyFile.getAbsolutePath() + "] deleted, rebuilding"); rebuildRouterInfo(); @@ -86,14 +79,10 @@ public class RebuildRouterInfoJob extends JobImpl { _log.debug("Rebuilding the new router info"); boolean fullRebuild = false; RouterInfo info = null; - String infoFilename = getContext().router().getConfigSetting(Router.PROP_INFO_FILENAME); - if (infoFilename == null) - infoFilename = Router.PROP_INFO_FILENAME_DEFAULT; - - String keyFilename = getContext().router().getConfigSetting(Router.PROP_KEYS_FILENAME); - if (keyFilename == null) - keyFilename = Router.PROP_KEYS_FILENAME_DEFAULT; - File keyFile = new File(keyFilename); + String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT); + File infoFile = new File(getContext().getRouterDir(), infoFilename); + String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT); + File keyFile = new File(getContext().getRouterDir(), keyFilename); if (keyFile.exists()) { // ok, no need to rebuild a brand new identity, just update what we can @@ -146,7 +135,7 @@ public class RebuildRouterInfoJob extends JobImpl { FileOutputStream fos = null; try { - fos = new FileOutputStream(infoFilename); + fos = new FileOutputStream(infoFile); info.writeBytes(fos); } catch (DataFormatException dfe) { _log.error("Error rebuilding the router information", dfe); diff --git a/router/java/src/net/i2p/router/transport/GeoIP.java b/router/java/src/net/i2p/router/transport/GeoIP.java index a7da9fad8..fb4984978 100644 --- a/router/java/src/net/i2p/router/transport/GeoIP.java +++ b/router/java/src/net/i2p/router/transport/GeoIP.java @@ -130,7 +130,8 @@ public class GeoIP { * */ private void readCountryFile() { - File GeoFile = new File(GEOIP_DIR_DEFAULT, COUNTRY_FILE_DEFAULT); + File GeoFile = new File(_context.getBaseDir(), GEOIP_DIR_DEFAULT); + GeoFile = new File(GeoFile, COUNTRY_FILE_DEFAULT); if (GeoFile == null || (!GeoFile.exists())) { if (_log.shouldLog(Log.WARN)) _log.warn("Country file not found: " + GeoFile.getAbsolutePath()); @@ -188,7 +189,8 @@ public class GeoIP { * */ private String[] readGeoIPFile(Long[] search) { - File GeoFile = new File(GEOIP_DIR_DEFAULT, GEOIP_FILE_DEFAULT); + File GeoFile = new File(_context.getBaseDir(), GEOIP_DIR_DEFAULT); + GeoFile = new File(GeoFile, GEOIP_FILE_DEFAULT); if (GeoFile == null || (!GeoFile.exists())) { if (_log.shouldLog(Log.WARN)) _log.warn("GeoIP file not found: " + GeoFile.getAbsolutePath()); From fd4e57aafca56a8b653c06af3e736aafbf8d2c9f Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 6 Jun 2009 15:36:06 +0000 Subject: [PATCH 02/58] * Console: - More conversions to getBaseDir() * Router: - Improve installUpdates() error handling * BrowserLauncher: - Use temp dir --- apps/routerconsole/jsp/help.jsp | 3 +- apps/routerconsole/jsp/index.jsp | 6 ++-- apps/routerconsole/jsp/nav.jsp | 5 +-- .../src/net/i2p/apps/systray/UrlLauncher.java | 8 +++-- build.xml | 2 +- router/java/src/net/i2p/router/Router.java | 31 +++++++++++++------ 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/apps/routerconsole/jsp/help.jsp b/apps/routerconsole/jsp/help.jsp index 68f1b6245..a100729da 100644 --- a/apps/routerconsole/jsp/help.jsp +++ b/apps/routerconsole/jsp/help.jsp @@ -184,7 +184,8 @@ client applications can be found on our d

Release history

- + <% File fpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getBaseDir(), "history.txt"); %> + diff --git a/apps/routerconsole/jsp/index.jsp b/apps/routerconsole/jsp/index.jsp index ce820f5b7..f1cd4f78f 100644 --- a/apps/routerconsole/jsp/index.jsp +++ b/apps/routerconsole/jsp/index.jsp @@ -19,7 +19,8 @@ if (System.getProperty("router.consoleNonce") == null) {
- + <% File fpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getBaseDir(), "docs/news.xml"); %> + @@ -30,7 +31,8 @@ if (System.getProperty("router.consoleNonce") == null) {
- <% if (new File("docs/toolbar.html").exists()) { %> + <% File path = new File(net.i2p.I2PAppContext.getGlobalContext().getBaseDir(), "docs/toolbar.html"); + if (path.exists()) { %> - + <% } else { %> diff --git a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java index c7524054e..55a7b5277 100644 --- a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java +++ b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java @@ -17,6 +17,7 @@ import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; +import net.i2p.I2PAppContext; import net.i2p.util.ShellCommand; /** @@ -68,10 +69,11 @@ public class UrlLauncher { String browserString = "\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" -nohome"; BufferedReader bufferedReader = null; - _shellCommand.executeSilentAndWait("regedit /E browser.reg \"HKEY_CLASSES_ROOT\\http\\shell\\open\\command\""); + File foo = new File(I2PAppContext.getGlobalContext().getTempDir(), "browser.reg"); + _shellCommand.executeSilentAndWait("regedit /E \"" + foo.getAbsolutePath() + "\" \"HKEY_CLASSES_ROOT\\http\\shell\\open\\command\""); try { - bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("browser.reg"), "UTF-16")); + bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(foo), "UTF-16")); for (String line; (line = bufferedReader.readLine()) != null; ) { if (line.startsWith("@=")) { // we should really use the whole line and replace %1 with the url @@ -86,7 +88,7 @@ public class UrlLauncher { } catch (IOException e) { // No worries. } - new File("browser.reg").delete(); + foo.delete(); } catch (Exception e) { // Defaults to IE. } finally { diff --git a/build.xml b/build.xml index 8fadcf831..0dbd9fa2b 100644 --- a/build.xml +++ b/build.xml @@ -360,7 +360,7 @@ - + diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 35518feda..8c918d93f 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -1076,6 +1076,7 @@ public class Router { * Unzip update file found in the router dir OR base dir, to the base dir * * If we can't write to the base dir, complain. + * Note: _log not available here. */ private void installUpdates() { File updateFile = new File(_context.getRouterDir(), UPDATE_FILE); @@ -1085,12 +1086,11 @@ public class Router { exists = updateFile.exists(); } if (exists) { + // do a simple permissions test, if it fails leave the file in place and don't restart File test = new File(_context.getBaseDir(), "history.txt"); - if ((!test.canWrite()) || (!_context.getBaseDir().canWrite())) { - String msg = "ERROR: No write permissions on " + _context.getBaseDir() + - " to extract software update file"; - System.out.println(msg); - _log.log(Log.CRIT, msg); + if ((test.exists() && !test.canWrite()) || (!_context.getBaseDir().canWrite())) { + System.out.println("ERROR: No write permissions on " + _context.getBaseDir() + + " to extract software update file"); // carry on return; } @@ -1100,10 +1100,23 @@ public class Router { System.out.println("INFO: Update installed"); else System.out.println("ERROR: Update failed!"); - boolean deleted = updateFile.delete(); - if (!deleted) { - System.out.println("ERROR: Unable to delete the update file!"); - updateFile.deleteOnExit(); + if (!ok) { + // we can't leave the file in place or we'll continually restart, so rename it + File bad = new File(_context.getRouterDir(), "BAD-" + UPDATE_FILE); + boolean renamed = updateFile.renameTo(bad); + if (renamed) { + System.out.println("Moved update file to " + bad.getAbsolutePath()); + } else { + System.out.println("Deleting file " + updateFile.getAbsolutePath()); + ok = true; // so it will be deleted + } + } + if (ok) { + boolean deleted = updateFile.delete(); + if (!deleted) { + System.out.println("ERROR: Unable to delete the update file!"); + updateFile.deleteOnExit(); + } } if (System.getProperty("wrapper.version") != null) System.out.println("INFO: Restarting after update"); From 290af4c187afb837c8a583b6cdaaeed0623e3754 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 11 Jun 2009 17:51:07 +0000 Subject: [PATCH 03/58] fix typo --- apps/routerconsole/jsp/viewtheme.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/routerconsole/jsp/viewtheme.jsp b/apps/routerconsole/jsp/viewtheme.jsp index 5785195c2..da0e05593 100644 --- a/apps/routerconsole/jsp/viewtheme.jsp +++ b/apps/routerconsole/jsp/viewtheme.jsp @@ -1,4 +1,4 @@ -% +<% /* * USE CAUTION WHEN EDITING * Trailing whitespace OR NEWLINE on the last line will cause From cadbe2c2c022458132915fe34f761f1a295b2ee3 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 13 Jun 2009 13:24:03 +0000 Subject: [PATCH 04/58] back to updaterWithJettyFixes --- build.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.xml b/build.xml index ae6224a70..19d7a0d56 100644 --- a/build.xml +++ b/build.xml @@ -202,8 +202,7 @@ - - + From 937de87dbf7dd53507efd5af2d428b13ca9f1c27 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 13 Jun 2009 13:34:37 +0000 Subject: [PATCH 05/58] post-0.7.4 cleanup --- .../src/net/i2p/router/StatisticsManager.java | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index f107c5e1e..9293a6234 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -87,14 +87,9 @@ public class StatisticsManager implements Service { if (_includePeerRankings) { long publishedUptime = _context.router().getUptime(); - boolean commentOutIn074 = RouterVersion.VERSION.equals("0.7.3"); // Don't publish these for first hour - if (publishedUptime > 62*60*1000) { - if (commentOutIn074) - includeThroughput(stats); - else - includeAverageThroughput(stats); - } + if (publishedUptime > 62*60*1000) + includeAverageThroughput(stats); //includeRate("router.invalidMessageTime", stats, new long[] { 10*60*1000 }); //includeRate("router.duplicateMessageId", stats, new long[] { 24*60*60*1000 }); //includeRate("tunnel.duplicateIV", stats, new long[] { 24*60*60*1000 }); @@ -245,27 +240,6 @@ public class StatisticsManager implements Service { stats.setProperty("stat_bandwidthReceiveBps.60m", str); } - private void includeThroughput(Properties stats) { - RateStat sendRate = _context.statManager().getRate("bw.sendRate"); - if (sendRate != null) { - if (_context.router().getUptime() > 60*60*1000) { - Rate r = sendRate.getRate(60*60*1000); - if (r != null) - stats.setProperty("stat_bandwidthSendBps.60m", num(r.getAverageValue()) + ';' + num(r.getExtremeAverageValue()) + ";0;0;"); - } - } - - RateStat recvRate = _context.statManager().getRate("bw.recvRate"); - if (recvRate != null) { - if (_context.router().getUptime() > 60*60*1000) { - Rate r = recvRate.getRate(60*60*1000); - if (r != null) - stats.setProperty("stat_bandwidthReceiveBps.60m", num(r.getAverageValue()) + ';' + num(r.getExtremeAverageValue()) + ";0;0;"); - } - } - } - - private String getPeriod(Rate rate) { return DataHelper.formatDuration(rate.getPeriod()); } private final String num(double num) { From 5c28125350c4afdd367bec7ef1675ef899515528 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 13 Jun 2009 13:46:11 +0000 Subject: [PATCH 06/58] add install path to eepget --- installer/install.xml | 3 ++- installer/resources/eepget | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/installer/install.xml b/installer/install.xml index d8ebb7d35..a4894b4e4 100644 --- a/installer/install.xml +++ b/installer/install.xml @@ -102,12 +102,13 @@ Base installation files + diff --git a/installer/resources/eepget b/installer/resources/eepget index d4ea04b34..080bcc8d8 100644 --- a/installer/resources/eepget +++ b/installer/resources/eepget @@ -1,4 +1,3 @@ #!/bin/sh -#export I2P=~i2p/i2p -export I2P=`dirname $0` -java -cp $I2P/lib/i2p.jar net.i2p.util.EepGet $* +I2P="%INSTALL_PATH" +java -cp "$I2P/lib/i2p.jar" net.i2p.util.EepGet "$@" From a16bcf8e514ea1a505e826fc0e0d05f26acdcbd1 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 13 Jun 2009 14:16:12 +0000 Subject: [PATCH 07/58] pid dir defaults to system temp dir --- core/java/src/net/i2p/I2PAppContext.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index 56f774071..52d0c4170 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -178,12 +178,12 @@ public class I2PAppContext { * Base i2p.dir.base getBaseDir() lib/, webapps/, docs/, geoip/, licenses/, ... * Temp i2p.dir.temp getTempDir() Temporary files * Config i2p.dir.config getConfigDir() *.config, hosts.txt, addressbook/, ... + * PID i2p.dir.pid getPIDDir() wrapper *.pid files, router.ping * * (the following all default to the same as Config) * * Router i2p.dir.router getRouterDir() netDb/, peerProfiles/, router.*, keyBackup/, ... * Log i2p.dir.log getLogDir() wrapper.log*, logs/ - * PID i2p.dir.pid getPIDDir() wrapper *.pid files, router.ping * App i2p.dir.app getAppDir() eepsite/, ... * * Note that we can't control where the wrapper puts its files. @@ -234,15 +234,12 @@ public class I2PAppContext { } else { _routerDir = _configDir; } + // pid defaults to system temp directory + s = getProperty("i2p.dir.pid", System.getProperty("java.io.tmpdir")); + _pidDir = new File(s); + if (!_pidDir.exists()) + _pidDir.mkdir(); // these all default to router - s = getProperty("i2p.dir.pid"); - if (s != null) { - _pidDir = new File(s); - if (!_pidDir.exists()) - _pidDir.mkdir(); - } else { - _pidDir = _routerDir; - } s = getProperty("i2p.dir.log"); if (s != null) { _logDir = new File(s); From 24daf006161813f76175cf1cd2540686051974c6 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 13 Jun 2009 21:04:27 +0000 Subject: [PATCH 08/58] * i2prouter: - Don't cd to script location, no longer required * RouterLaunch: - If no wrapper, put wrapper.log in system temp dir unless specified with -Dwrapper.logfile=/path/to/wrapper.log or it already exists in CWD (for backward compatibility) - Append rather than replace wrapper.log - Pass wrapper log location to router as a property, so that logs.jsp can find it * logs.jsp: - Get wrapper log location from a property too * runplain.sh: - Add path substitution to runplain.sh on install - Pass I2P base dir to the router as a property * wrapper.config: - Put wrapper.log in system temp dir for new installs - Pass I2P base dir to the router as a property * WorkingDir: - Don't migrate an existing install by default - Never migrate the data (too hard) --- .../src/net/i2p/router/web/LogsHelper.java | 17 +++++-- core/java/src/net/i2p/util/WorkingDir.java | 34 +++++++++---- installer/install.xml | 1 + installer/resources/i2prouter | 51 ++++--------------- installer/resources/runplain.sh | 16 ++++-- installer/resources/wrapper.config | 11 +++- .../java/src/net/i2p/router/RouterLaunch.java | 28 +++++++++- 7 files changed, 94 insertions(+), 64 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java index a4896cdb8..9aee053f5 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java @@ -17,16 +17,23 @@ public class LogsHelper extends HelperBase { } public String getServiceLogs() { - // look in new and old place - File f = new File(_context.getLogDir(), "wrapper.log"); - if (!f.exists()) - f = new File(_context.getBaseDir(), "wrapper.log"); + // RouterLaunch puts the location here if no wrapper + String path = System.getProperty("wrapper.logfile"); + File f; + if (path != null) { + f = new File(path); + } else { + // look in new and old places + f = new File(System.getProperty("java.io.tmpdir"), "wrapper.log"); + if (!f.exists()) + f = new File(_context.getBaseDir(), "wrapper.log"); + } String str = FileUtil.readTextFile(f.getAbsolutePath(), 250, false); if (str == null) return ""; else { str = str.replaceAll("<", "<").replaceAll(">", ">"); - return "
" + str + "
"; + return "Location: " + f.getAbsolutePath() + "
" + str + "
"; } } diff --git a/core/java/src/net/i2p/util/WorkingDir.java b/core/java/src/net/i2p/util/WorkingDir.java index 4e47f6458..0ba73f391 100644 --- a/core/java/src/net/i2p/util/WorkingDir.java +++ b/core/java/src/net/i2p/util/WorkingDir.java @@ -52,7 +52,7 @@ public class WorkingDir { * Only call this once on router invocation. * Caller should store the return value for future reference. */ - public static String getWorkingDir(boolean migrateOldData) { + public static String getWorkingDir(boolean migrateOldConfig) { String dir = System.getProperty(PROP_WORKING_DIR); boolean isWindows = System.getProperty("os.name").startsWith("Win"); File dirf = null; @@ -73,6 +73,18 @@ public class WorkingDir { String cwd = System.getProperty(PROP_BASE_DIR); if (cwd == null) cwd = System.getProperty("user.dir"); + + // Check for a hosts.txt file, if it exists then I2P is there + File oldDirf = new File(cwd); + File test = new File(oldDirf, "hosts.txt"); + if (!test.exists()) { + System.err.println("ERROR - Cannot find I2P installation in " + cwd + + " - Will probably be just a router with no apps or console at all!"); + // until we move reseeding from the console to the router, we + // won't be able to reseed, so we are probably doomed + return cwd; + } + // where we want to go String rv = dirf.getAbsolutePath(); if (dirf.exists()) { @@ -86,19 +98,19 @@ public class WorkingDir { return cwd; } - // Check for a hosts.txt file, if it exists then I2P is there - File oldDirf = new File(cwd); - File test = new File(oldDirf, "hosts.txt"); - if (!test.exists()) { - System.err.println("ERROR - Cannot find I2P installation in " + cwd); - return cwd; - } - - // Check for a router.keys file, if it exists it's an old install, + // Check for a router.keys file or logs dir, if either exists it's an old install, // and only migrate the data files if told to do so + // (router.keys could be deleted later by a killkeys()) test = new File(oldDirf, "router.keys"); boolean oldInstall = test.exists(); - migrateOldData &= oldInstall; + if (!oldInstall) { + test = new File(oldDirf, "logs"); + oldInstall = test.exists(); + } + // keep everything where it is, in one place... + if (oldInstall && !migrateOldConfig) + return cwd; + boolean migrateOldData = false; // this is a terrible idea // Do the copying if (migrateOldData) diff --git a/installer/install.xml b/installer/install.xml index a4894b4e4..1abfda98d 100644 --- a/installer/install.xml +++ b/installer/install.xml @@ -109,6 +109,7 @@ + diff --git a/installer/resources/i2prouter b/installer/resources/i2prouter index b5c292f19..c8bab7911 100644 --- a/installer/resources/i2prouter +++ b/installer/resources/i2prouter @@ -8,28 +8,31 @@ # if you have changed the default location set in the # wrapper configuration file. # -# Note that (percent)INSTALL_PATH and (percent)SYSTEM_java_io_tmpdir -# should have been replaced by -# the izpack installer. If you did not run the installer, -# replace them with the appropriate path. #----------------------------------------------------------------------------- # These settings can be modified to fit the needs of your application +# Paths +# Note that (percent)INSTALL_PATH and (percent)SYSTEM_java_io_tmpdir +# should have been replaced by the izpack installer. +# If you did not run the installer, replace them with the appropriate path. +I2P="%INSTALL_PATH" +I2PTEMP="%SYSTEM_java_io_tmpdir" + # Application APP_NAME="i2p" APP_LONG_NAME="I2P Service" # Wrapper -WRAPPER_CMD="%INSTALL_PATH/i2psvc" -WRAPPER_CONF="%INSTALL_PATH/wrapper.config" +WRAPPER_CMD="$I2P/i2psvc" +WRAPPER_CONF="$I2P/wrapper.config" # Priority at which to run the wrapper. See "man nice" for valid priorities. # nice is only used if a priority is specified. PRIORITY= # Location of the pid file. -PIDDIR="%SYSTEM_java_io_tmpdir" +PIDDIR="$I2PTEMP" # If uncommented, causes the Wrapper to be shutdown using an anchor file. # When launched with the 'start' command, it will also ignore all INT and @@ -51,40 +54,6 @@ PIDDIR="%SYSTEM_java_io_tmpdir" # Do not modify anything beyond this point #----------------------------------------------------------------------------- -# Get the fully qualified path to the script -case $0 in - /*) - SCRIPT="$0" - ;; - *) - PWD=`pwd` - SCRIPT="$PWD/$0" - ;; -esac - -# Change spaces to ":" so the tokens can be parsed. -SCRIPT=`echo $SCRIPT | sed -e 's; ;:;g'` -# Get the real path to this script, resolving any symbolic links -TOKENS=`echo $SCRIPT | sed -e 's;/; ;g'` -REALPATH= -for C in $TOKENS; do - REALPATH="$REALPATH/$C" - while [ -h "$REALPATH" ] ; do - LS="`ls -ld "$REALPATH"`" - LINK="`expr "$LS" : '.*-> \(.*\)$'`" - if expr "$LINK" : '/.*' > /dev/null; then - REALPATH="$LINK" - else - REALPATH="`dirname "$REALPATH"`""/$LINK" - fi - done -done -# Change ":" chars back to spaces. -REALPATH=`echo $REALPATH | sed -e 's;:; ;g'` - -# Change the current directory to the location of the script -cd "`dirname "$REALPATH"`" - # Process ID ANCHORFILE="$PIDDIR/$APP_NAME.anchor" PIDFILE="$PIDDIR/$APP_NAME.pid" diff --git a/installer/resources/runplain.sh b/installer/resources/runplain.sh index 25221f513..644bdadcf 100644 --- a/installer/resources/runplain.sh +++ b/installer/resources/runplain.sh @@ -5,9 +5,17 @@ # probably not enough for i2p. # You should really use the i2prouter script instead. # -export CP=. ; for j in lib/* ; do export CP=$CP:$j ; done; + +# Paths +# Note that (percent)INSTALL_PATH and (percent)SYSTEM_java_io_tmpdir +# should have been replaced by the izpack installer. +# If you did not run the installer, replace them with the appropriate path. +I2P="%INSTALL_PATH" +I2PTEMP="%SYSTEM_java_io_tmpdir" + +export CP="$I2P" ; for j in "$I2P/lib/*" ; do export CP="$CP:$j" ; done; JAVA=java -JAVAOPTS="-Djava.library.path=.:lib -DloggerFilenameOverride=logs/log-router-@.txt" -nohup $JAVA -cp $CP $JAVAOPTS net.i2p.router.RouterLaunch > /dev/null 2>&1 & -echo $! > router.pid +JAVAOPTS="-Djava.library.path=$I2P:$I2P/lib -Di2p.dir.base=$I2P -DloggerFilenameOverride=logs/log-router-@.txt" +nohup $JAVA -cp "$CP" $JAVAOPTS net.i2p.router.RouterLaunch > /dev/null 2>&1 & +echo $! > "$I2PTEMP/router.pid" diff --git a/installer/resources/wrapper.config b/installer/resources/wrapper.config index b14bc8288..03efe22fb 100644 --- a/installer/resources/wrapper.config +++ b/installer/resources/wrapper.config @@ -51,11 +51,12 @@ wrapper.java.additional.1=-DloggerFilenameOverride=logs/log-router-@.txt wrapper.java.additional.2=-Dorg.mortbay.http.Version.paranoid=true wrapper.java.additional.3=-Dorg.mortbay.util.FileResource.checkAliases=false wrapper.java.additional.4=-Dorg.mortbay.xml.XmlParser.NotValidating=true +wrapper.java.additional.5=-Di2p.dir.base=$INSTALL_PATH # Uncomment this for better performance. # If it doesn't work, server mode is not available in your JVM. # This may not be required if your machine is already "server-class". # See http://java.sun.com/j2se/1.5.0/docs/guide/vm/server-class.html -#wrapper.java.additional.5=-server +#wrapper.java.additional.6=-server # Initial Java Heap Size (in MB) #wrapper.java.initmemory=4 @@ -86,7 +87,13 @@ wrapper.console.format=PM wrapper.console.loglevel=INFO # Log file to use for wrapper output logging. -wrapper.logfile=wrapper.log +# You may wish to change this on linux so the log is +# preserved across OS restarts. +# If you do change it, add the following line above to +# tell the router where to find the wrapper log +# (change X to the next available number) +# wrapper.java.additional.X=-Dwrapper.logfile=/path/to/wrapper.log +wrapper.logfile=$SYSTEM_java_io_tmpdir/wrapper.log # Format of output for the log file. (See docs for formats) wrapper.logfile.format=LPTM diff --git a/router/java/src/net/i2p/router/RouterLaunch.java b/router/java/src/net/i2p/router/RouterLaunch.java index 6aab4e8ac..13ee2f3cf 100644 --- a/router/java/src/net/i2p/router/RouterLaunch.java +++ b/router/java/src/net/i2p/router/RouterLaunch.java @@ -1,13 +1,39 @@ package net.i2p.router; +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; +/** + * This is the class called by the runplain.sh script on linux + * and the i2p.exe launcher on Windows. + * (i.e. no wrapper) + * + * If there is no -Dwrapper.log=/path/to/wrapper.log on the java command line + * to specify a log file, check for existence of wrapper.log in CWD, + * for backward compatibility in old installations (don't move it). + * Otherwise, use (system temp dir)/wrapper.log. + * Create if it doesn't exist, and append to it if it does. + * Put the location in the environment as an absolute path, so logs.jsp can find it. + */ public class RouterLaunch { + private static final String PROP_WRAPPER_LOG = "wrapper.logfile"; + private static final String DEFAULT_WRAPPER_LOG = "wrapper.log"; + public static void main(String args[]) { + String path = System.getProperty(PROP_WRAPPER_LOG); + File logfile; + if (path != null) { + logfile = new File(path); + } else { + logfile = new File(DEFAULT_WRAPPER_LOG); + if (!logfile.exists()) + logfile = new File(System.getProperty("java.io.tmpdir"), DEFAULT_WRAPPER_LOG); + } + System.setProperty(PROP_WRAPPER_LOG, logfile.getAbsolutePath()); try { - System.setOut(new PrintStream(new FileOutputStream("wrapper.log"))); + System.setOut(new PrintStream(new FileOutputStream(logfile, true))); } catch (IOException ioe) { ioe.printStackTrace(); } From e5ec72b09b411e12cc814efa9000ba38845a96d8 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 13 Jun 2009 23:47:08 +0000 Subject: [PATCH 09/58] * ConsoleRunner: - Fix webapps file path * SusiDNS: - Fix addressbook file path * Systray: - Fix NPE if no config file - Fix config file path * WorkingDir: - Modify clients.config so jetty can find the jetty.xml file - Rip out all the existing-installation migration code - Rip out migration code now done by izpack parsable - Fix copy of empty directories --- .../i2p/router/web/RouterConsoleRunner.java | 15 +- .../src/i2p/susi/dns/AddressbookBean.java | 10 +- .../src/net/i2p/apps/systray/ConfigFile.java | 18 +- .../src/net/i2p/apps/systray/SysTray.java | 6 +- core/java/src/net/i2p/util/WorkingDir.java | 222 ++---------------- 5 files changed, 53 insertions(+), 218 deletions(-) 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 f58aeb308..25b990f04 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -74,6 +74,19 @@ public class RouterConsoleRunner { props.setProperty(PREFIX + ROUTERCONSOLE + ENABLED, "true"); rewrite = true; } + + // Get an absolute path with a trailing slash for the webapps dir + // We assume relative to the base install dir for backward compatibility + File app = new File(_webAppsDir); + if (!app.isAbsolute()) { + app = new File(I2PAppContext.getGlobalContext().getBaseDir(), _webAppsDir); + try { + _webAppsDir = app.getCanonicalPath(); + } catch (IOException ioe) {} + } + if (!_webAppsDir.endsWith("/")) + _webAppsDir += '/'; + try { StringTokenizer tok = new StringTokenizer(_listenHost, " ,"); int boundAddresses = 0; @@ -99,7 +112,7 @@ public class RouterConsoleRunner { tmpdir.mkdir(); wac.setTempDirectory(tmpdir); initialize(wac); - File dir = new File(I2PAppContext.getGlobalContext().getBaseDir(), _webAppsDir); + File dir = new File(_webAppsDir); String fileNames[] = dir.list(WarFilenameFilter.instance()); if (fileNames != null) { for (int i = 0; i < fileNames.length; i++) { diff --git a/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java b/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java index 27971d47d..a5dcf86cf 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java @@ -24,6 +24,7 @@ package i2p.susi.dns; +import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -110,9 +111,12 @@ public class AddressbookBean { loadConfig(); String filename = properties.getProperty( getBook() + "_addressbook" ); - if (filename.startsWith("../")) - return filename.substring(3); - return ConfigBean.addressbookPrefix + filename; + // clean up the ../ with getCanonicalPath() + File path = new File(ConfigBean.addressbookPrefix, filename); + try { + return path.getCanonicalPath(); + } catch (IOException ioe) {} + return filename; } private Object[] entries; public Object[] getEntries() diff --git a/apps/systray/java/src/net/i2p/apps/systray/ConfigFile.java b/apps/systray/java/src/net/i2p/apps/systray/ConfigFile.java index 952ecba82..3a1a3edb4 100644 --- a/apps/systray/java/src/net/i2p/apps/systray/ConfigFile.java +++ b/apps/systray/java/src/net/i2p/apps/systray/ConfigFile.java @@ -60,11 +60,10 @@ public class ConfigFile { _properties.load(fileInputStream); } catch (Exception e) { rv = false; - } - try { - fileInputStream.close(); - } catch (IOException e) { - // No worries. + } finally { + if (fileInputStream != null) { + try { fileInputStream.close(); } catch (IOException e) {} + } } return rv; } @@ -78,11 +77,10 @@ public class ConfigFile { _properties.store(fileOutputStream, null); } catch (Exception e) { rv = false; - } - try { - fileOutputStream.close(); - } catch (IOException e) { - // No worries. + } finally { + if (fileOutputStream != null) { + try { fileOutputStream.close(); } catch (IOException e) {} + } } return rv; } diff --git a/apps/systray/java/src/net/i2p/apps/systray/SysTray.java b/apps/systray/java/src/net/i2p/apps/systray/SysTray.java index 652ff6677..f7e649942 100644 --- a/apps/systray/java/src/net/i2p/apps/systray/SysTray.java +++ b/apps/systray/java/src/net/i2p/apps/systray/SysTray.java @@ -10,9 +10,12 @@ package net.i2p.apps.systray; import java.awt.Frame; +import java.io.File; +import net.i2p.I2PAppContext; import net.i2p.util.SimpleScheduler; import net.i2p.util.SimpleTimer; + import snoozesoft.systray4j.SysTrayMenu; import snoozesoft.systray4j.SysTrayMenuEvent; import snoozesoft.systray4j.SysTrayMenuIcon; @@ -36,7 +39,8 @@ public class SysTray implements SysTrayMenuListener { private static UrlLauncher _urlLauncher = new UrlLauncher(); static { - if (!_configFile.init("systray.config")) { + File config = new File(I2PAppContext.getGlobalContext().getConfigDir(), "systray.config"); + if (!_configFile.init(config.getAbsolutePath())) { _configFile.setProperty("browser", "default"); _configFile.setProperty("port", "7657"); } diff --git a/core/java/src/net/i2p/util/WorkingDir.java b/core/java/src/net/i2p/util/WorkingDir.java index 0ba73f391..a3fa6ca23 100644 --- a/core/java/src/net/i2p/util/WorkingDir.java +++ b/core/java/src/net/i2p/util/WorkingDir.java @@ -120,36 +120,11 @@ public class WorkingDir { boolean success = migrate(MIGRATE_BASE, oldDirf, dirf); // this one must be after MIGRATE_BASE success &= migrateJettyXml(oldDirf, dirf); - success &= migrateWrapperConfig(oldDirf, dirf); - if (migrateOldData) { - success &= migrate(MIGRATE_DATA, oldDirf, dirf); - success &= migrateI2PTunnelKeys(oldDirf, dirf); - success &= migrateSnark(oldDirf, dirf); - // new installs will have updated scripts left in the install dir - // don't bother migrating runplain.sh or i2prouter.bat - if (!isWindows) - success &= migrateI2prouter(oldDirf, dirf); - } else if (!oldInstall) { - // copy the default i2psnark.config over - success &= migrate("i2psnark.config", oldDirf, dirf); - } + success &= migrateClientsConfig(oldDirf, dirf); // Report success or failure if (success) { System.err.println("Successfully copied data files to new user directory " + rv); - if (migrateOldData) { - System.err.println("Libraries and other files remain in the old directory " + cwd + ", do not remove them."); - System.err.println("You should manually move any non-standard files, such as additional eepsite directories and key files"); - System.err.println("After verifying that all is working, you may delete the following data files and directories in " + - cwd + ": " + MIGRATE_DATA.replace(',', ' ') + " i2psnark.config tmp work"); - if (System.getProperty("wrapper.version") != null) - System.err.println("Note that until you shutdown your router completely and restart, the wrapper will continue" + - " to log to the old wrapper logs in " + cwd); - if (!isWindows) - System.err.println("From now on, you should now use the i2prouter" + - " script in the " + rv + " directory to start i2p." + - " You may copy or move this script elsewhere, you need not run it from that directory."); - } return rv; } else { System.err.println("FAILED copy of some or all data files to new directory " + rv); @@ -173,27 +148,9 @@ public class WorkingDir { // base install - files // We don't currently have a default router.config or logger.config in the base distribution, // but distros might put one in - "blocklist.txt,clients.config,hosts.txt,i2ptunnel.config,jetty-i2psnark.xml," + + "blocklist.txt,hosts.txt,i2psnark.config,i2ptunnel.config,jetty-i2psnark.xml," + "logger.config,router.config,systray.config"; - /** - * files and directories from an old single-directory installation to copy over - NOT including snark - * None of these should be included in i2pupdate.zip - * - * The user can be advised to delete these from the old location - */ - private static final String MIGRATE_DATA = - // post install - dirs - // not required to copy - tmp/, work/ - // addressbook included in MIGRATE_BASE above - "keyBackup,logs,netDb,peerProfiles," + - // post install - files - // not required to copy - prngseed.rnd - // logger.config and router.config included in MIGRATE_BASE above - "bob.config,privatehosts.txt,router.info,router.keys," + - "sam.keys,susimail.config,userhosts.txt,webapps.config," + - "wrapper.log,wrapper.log.1,wrapper.log.2"; - private static boolean migrate(String list, File olddir, File todir) { boolean rv = true; String files[] = list.split(","); @@ -208,132 +165,11 @@ public class WorkingDir { } /** - * Copy over the i2psnark.config file with modifications + * Copy over the clients.config file with modifications */ - private static boolean migrateSnark(File olddir, File todir) { - boolean rv = true; - File oldSnark = new File(olddir, "i2psnark"); - File newSnark = new File(todir, "i2psnark"); - File oldSnarkConfig = new File(olddir, "i2psnark.config"); - File newSnarkConfig = new File(todir, "i2psnark.config"); - boolean hasData = oldSnark.exists(); - if (hasData) { - File children[] = oldSnark.listFiles(); - hasData = children != null && children.length > 0; - } - if (oldSnarkConfig.exists()) { - if (hasData) { - // edit the snark config file to point to the old location, we aren't moving the data - try { - Properties props = new Properties(); - DataHelper.loadProps(props, oldSnarkConfig); - String dir = props.getProperty("i2psnark.dir"); - if (dir == null) - dir = "i2psnark"; - // change relative to absolute path - File f = new File(dir); - props.setProperty("i2psnark.dir", f.getAbsolutePath()); - DataHelper.storeProps(props, newSnarkConfig); - System.err.println("Copied i2psnark.config with modifications"); - } catch (IOException ioe) { - System.err.println("FAILED copy i2psnark.config"); - rv = false; - } - } else { - // copy the i2psnark config file over - copy(newSnarkConfig, todir); - System.err.println("Copied i2psnark.config"); - } - } else { - if (hasData) { - // data but no previous config file (unlikely) - make new config file - try { - Properties props = new Properties(); - File f = new File("i2psnark"); - props.setProperty("i2psnark.dir", f.getAbsolutePath()); - DataHelper.storeProps(props, newSnarkConfig); - } catch (IOException ioe) { - // ignore - } - } // else no config and no data - } - if (hasData) { - /************* - // crude attempt to detect same filesystem - if ((oldSnark.getAbsolutePath().startsWith("/home/") && newSnark.getAbsolutePath().startsWith("/home/")) || - (System.getProperty("os.name").toLowerCase.indexOf("windows") >= 0 && - oldSnark.getAbsolutePath().substring(0,1).equals(newSnark.getAbsolutePath().substring(0,1) && - oldSnark.getAbsolutePath().substring(1,2).equals(":\\") && - newSnark.getAbsolutePath().substring(1,2).equals(":\\"))) { - // OK, apparently in same file system - // move everything - } - **************/ - System.err.println("NOT moving the i2psnark data directory " + oldSnark.getAbsolutePath() + - " to the new directory " + newSnark.getAbsolutePath() + - ". You may move the directory contents manually WHILE I2P IS NOT RUNNING," + - " and edit the file " + newSnarkConfig.getAbsolutePath() + - " to configure i2psnark to use a different location by editing the i2psnark.dir configuration to be" + - " i2psnark.dir=" + oldSnark.getAbsolutePath() + - " and restart, or you may leave the i2psnark directory in its old location."); - } - return true; - } - - /** - * Copy over the i2prouter file with modifications - * The resulting script can be run from any location. - */ - private static boolean migrateI2prouter(File olddir, File todir) { - File oldFile = new File(olddir, "i2prouter"); - File newFile = new File(todir, "i2prouter"); - FileInputStream in = null; - PrintWriter out = null; - try { - in = new FileInputStream(oldFile); - out = new PrintWriter(new BufferedWriter(new FileWriter(newFile))); - boolean firstTime = true; - String s = null; - while ((s = DataHelper.readLine(in)) != null) { - if (s.equals("WRAPPER_CMD=\"./i2psvc\"")) { - // i2psvc in the old location - File f = new File("i2psvc"); - s = "WRAPPER_CMD=\"" + f.getAbsolutePath() + "\""; - } else if(s.equals("WRAPPER_CONF=\"wrapper.config\"")) { - // wrapper.config the new location - File f = new File(todir, "wrapper.config"); - s = "WRAPPER_CONF=\"" + f.getAbsolutePath() + "\""; - } else if(s.equals("PIDDIR=\".\"")) { - // i2p.pid in the new location - s = "PIDDIR=\"" + todir.getAbsolutePath() + "\""; - } - out.println(s); - if (firstTime) { - // first line was #!/bin/sh, so had to wait until now - out.println("# Modified by I2P User dir migration script"); - firstTime = false; - } - } - System.err.println("Copied i2prouter with modifications"); - return true; - } catch (IOException ioe) { - if (in != null) { - System.err.println("FAILED copy i2prouter"); - return false; - } - return true; - } finally { - if (in != null) try { in.close(); } catch (IOException ioe) {} - if (out != null) out.close(); - } - } - - /** - * Copy over the wrapper.config file with modifications - */ - private static boolean migrateWrapperConfig(File olddir, File todir) { - File oldFile = new File(olddir, "wrapper.config"); - File newFile = new File(todir, "wrapper.config"); + private static boolean migrateClientsConfig(File olddir, File todir) { + File oldFile = new File(olddir, "clients.config"); + File newFile = new File(todir, "clients.config"); FileInputStream in = null; PrintWriter out = null; try { @@ -341,36 +177,17 @@ public class WorkingDir { out = new PrintWriter(new BufferedWriter(new FileWriter(newFile))); out.println("# Modified by I2P User dir migration script"); String s = null; - // Don't use replaceFirst because backslashes in the replacement string leads to havoc - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4689750 - // "Note that backslashes and dollar signs in the replacement string may cause the results - // to be different than if it were being treated as a literal replacement string. - // Dollar signs may be treated as references to captured subsequences as described above, - // and backslashes are used to escape literal characters in the replacement string." while ((s = DataHelper.readLine(in)) != null) { - if (s.startsWith("wrapper.java.classpath.")) { - // libraries in the old location - s = s.replace("=lib/", '=' + olddir.getAbsolutePath() + File.separatorChar + "lib" + File.separatorChar); - } else if (s.startsWith("wrapper.java.library.path.")) { - // libraries in the old location - if (s.contains("=.")) - s = s.replace("=.", '=' + olddir.getAbsolutePath()); - else if (s.contains("=lib")) - s = s.replace("=lib", '=' + olddir.getAbsolutePath() + File.separatorChar + "lib"); - } else if (s.startsWith("wrapper.logfile=wrapper.log")) { - // wrapper logs in the new location - s = s.replace("=", '=' + todir.getAbsolutePath() + File.separatorChar); - } else if (s.startsWith("wrapper.pidfile=i2p.pid")) { - // i2p.pid in the new location - s = s.replace("=", '=' + todir.getAbsolutePath() + File.separatorChar); + if (s.endsWith("=eepsite/jetty.xml")) { + s = s.replace("=eepsite", '=' + todir.getAbsolutePath() + File.separatorChar + "eepsite"); } out.println(s); } - System.err.println("Copied wrapper.config with modifications"); + System.err.println("Copied clients.config with modifications"); return true; } catch (IOException ioe) { if (in != null) { - System.err.println("FAILED copy wrapper.config"); + System.err.println("FAILED copy clients.config"); return false; } return false; @@ -417,16 +234,6 @@ public class WorkingDir { } } - /** - * Relatively recent default i2ptunnel key file name - */ - private static boolean migrateI2PTunnelKeys(File olddir, File todir) { - for (int i = 0; i < 100; i++) { - copy(new File(olddir, "i2ptunnel" + i + "-privKeys.dat"), todir); - } - return true; - } - /** * Recursive copy a file or dir to a dir * @@ -442,6 +249,7 @@ public class WorkingDir { System.err.println("FAILED copy " + src.getPath()); return false; } + System.err.println("Created " + targetDir.getPath()); } File targetFile = new File(targetDir, src.getName()); if (!src.isDirectory()) @@ -451,6 +259,14 @@ public class WorkingDir { System.err.println("FAILED copy " + src.getPath()); return false; } + // make it here so even empty dirs get copied + if (!targetFile.exists()) { + if (!targetFile.mkdir()) { + System.err.println("FAILED copy " + src.getPath()); + return false; + } + System.err.println("Created " + targetFile.getPath()); + } boolean rv = true; for (int i = 0; i < children.length; i++) { rv &= copy(children[i], targetFile); From 112ddc71566f63ba00ebd3c9e97d9780d27b5e5b Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 14 Jun 2009 01:49:27 +0000 Subject: [PATCH 10/58] * jbigi, cpuid: - Extract files from jar to temp dir, load from that dir, then copy to the base dir if we have permissions (and failing silently if we don't), so we have optimized libs and no complaints when we have a read-only base dir. --- .../freenet/support/CPUInformation/CPUID.java | 16 ++++++++++++---- core/java/src/net/i2p/util/FileUtil.java | 16 +++++++++++++--- core/java/src/net/i2p/util/NativeBigInteger.java | 16 ++++++++++++---- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/core/java/src/freenet/support/CPUInformation/CPUID.java b/core/java/src/freenet/support/CPUInformation/CPUID.java index 58469c6d7..e145c3795 100644 --- a/core/java/src/freenet/support/CPUInformation/CPUID.java +++ b/core/java/src/freenet/support/CPUInformation/CPUID.java @@ -10,6 +10,7 @@ import java.io.InputStream; import java.net.URL; import net.i2p.I2PAppContext; +import net.i2p.util.FileUtil; /** * @author Iakin @@ -484,8 +485,11 @@ public class CPUID { * *

This is a pretty ugly hack, using the general technique illustrated by the * onion FEC libraries. It works by pulling the resource, writing out the - * byte stream to a temporary file, loading the native library from that file, - * then deleting the file.

+ * byte stream to a temporary file, loading the native library from that file. + * We then attempt to copy the file from the temporary dir to the base install dir, + * so we don't have to do this next time - but we don't complain if it fails, + * so we transparently support read-only base dirs. + *

* * @return true if it was loaded successfully, else false * @@ -503,9 +507,10 @@ public class CPUID { File outFile = null; FileOutputStream fos = null; + String filename = libPrefix + "jcpuid" + libSuffix; try { InputStream libStream = resource.openStream(); - outFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), libPrefix + "jcpuid" + libSuffix); + outFile = new File(I2PAppContext.getGlobalContext().getTempDir(), filename); fos = new FileOutputStream(outFile); // wtf this was 4096*1024 which is really excessive for a roughly 4KB file byte buf[] = new byte[4096]; @@ -517,7 +522,6 @@ public class CPUID { fos.close(); fos = null; System.load(outFile.getAbsolutePath());//System.load requires an absolute path to the lib - return true; } catch (UnsatisfiedLinkError ule) { if (_doLog) { System.err.println("ERROR: The resource " + resourceName @@ -536,6 +540,10 @@ public class CPUID { try { fos.close(); } catch (IOException ioe) {} } } + // copy to install dir, ignore failure + File newFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), filename); + FileUtil.copy(outFile.getAbsolutePath(), newFile.getAbsolutePath(), false, true); + return true; } private static final String getResourceName() diff --git a/core/java/src/net/i2p/util/FileUtil.java b/core/java/src/net/i2p/util/FileUtil.java index 05346ccfe..b6140b6e4 100644 --- a/core/java/src/net/i2p/util/FileUtil.java +++ b/core/java/src/net/i2p/util/FileUtil.java @@ -201,9 +201,18 @@ public class FileUtil { } } - - /** return true if it was copied successfully */ + /** + * @return true if it was copied successfully + */ public static boolean copy(String source, String dest, boolean overwriteExisting) { + return copy(source, dest, overwriteExisting, false); + } + + /** + * @param quiet don't log fails to wrapper log if true + * @return true if it was copied successfully + */ + public static boolean copy(String source, String dest, boolean overwriteExisting, boolean quiet) { File src = new File(source); File dst = new File(dest); @@ -226,7 +235,8 @@ public class FileUtil { out.close(); return true; } catch (IOException ioe) { - ioe.printStackTrace(); + if (!quiet) + ioe.printStackTrace(); return false; } } diff --git a/core/java/src/net/i2p/util/NativeBigInteger.java b/core/java/src/net/i2p/util/NativeBigInteger.java index bafaa5e5c..44101afd9 100644 --- a/core/java/src/net/i2p/util/NativeBigInteger.java +++ b/core/java/src/net/i2p/util/NativeBigInteger.java @@ -24,6 +24,7 @@ import freenet.support.CPUInformation.IntelCPUInfo; import freenet.support.CPUInformation.UnknownCPUException; import net.i2p.I2PAppContext; +import net.i2p.util.FileUtil; import net.i2p.util.Log; /** @@ -516,8 +517,11 @@ public class NativeBigInteger extends BigInteger { * *

This is a pretty ugly hack, using the general technique illustrated by the * onion FEC libraries. It works by pulling the resource, writing out the - * byte stream to a temporary file, loading the native library from that file, - * then deleting the file.

+ * byte stream to a temporary file, loading the native library from that file. + * We then attempt to copy the file from the temporary dir to the base install dir, + * so we don't have to do this next time - but we don't complain if it fails, + * so we transparently support read-only base dirs. + *

* * @return true if it was loaded successfully, else false * @@ -538,9 +542,10 @@ public class NativeBigInteger extends BigInteger { File outFile = null; FileOutputStream fos = null; + String filename = _libPrefix + "jbigi" + _libSuffix; try { InputStream libStream = resource.openStream(); - outFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), _libPrefix + "jbigi" + _libSuffix); + outFile = new File(I2PAppContext.getGlobalContext().getTempDir(), filename); fos = new FileOutputStream(outFile); // wtf this was 4096*1024 which is really excessive for a roughly 50KB file byte buf[] = new byte[4096]; @@ -552,7 +557,6 @@ public class NativeBigInteger extends BigInteger { fos.close(); fos = null; System.load(outFile.getAbsolutePath()); //System.load requires an absolute path to the lib - return true; } catch (UnsatisfiedLinkError ule) { if (_doLog) { System.err.println("ERROR: The resource " + resourceName @@ -571,6 +575,10 @@ public class NativeBigInteger extends BigInteger { try { fos.close(); } catch (IOException ioe) {} } } + // copy to install dir, ignore failure + File newFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), filename); + FileUtil.copy(outFile.getAbsolutePath(), newFile.getAbsolutePath(), false, true); + return true; } private static final String getResourceName(boolean optimized) { From e8773f6a98be3755a75dfab9abbf699e8fc25015 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 14 Jun 2009 02:35:41 +0000 Subject: [PATCH 11/58] fix NPE for non-router invocations --- core/java/src/net/i2p/I2PAppContext.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index 52d0c4170..1080ee6ac 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -21,7 +21,6 @@ import net.i2p.crypto.KeyGenerator; import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SessionKeyManager; import net.i2p.crypto.TransientSessionKeyManager; - import net.i2p.data.DataHelper; import net.i2p.data.RoutingKeyGenerator; import net.i2p.stat.StatManager; import net.i2p.util.Clock; @@ -224,7 +223,6 @@ public class I2PAppContext { } else { _configDir = _baseDir; } - _configDir = new File(s); // router defaults to config s = getProperty("i2p.dir.router"); if (s != null) { From 17751ffd57944849a5db17b39e137ac531f9d42b Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 14 Jun 2009 13:00:23 +0000 Subject: [PATCH 12/58] * news.xml: - move from base to router dir --- apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java | 2 +- apps/routerconsole/jsp/index.jsp | 2 +- core/java/src/net/i2p/util/WorkingDir.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java index fd7662caf..90835b070 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java @@ -46,7 +46,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { _log = ctx.logManager().getLog(NewsFetcher.class); _instance = this; _lastFetch = 0; - _newsFile = new File(_context.getBaseDir(), NEWS_FILE); + _newsFile = new File(_context.getRouterDir(), NEWS_FILE); _tempFile = new File(_context.getTempDir(), TEMP_NEWS_FILE); updateLastFetched(); _lastUpdated = _lastFetch; diff --git a/apps/routerconsole/jsp/index.jsp b/apps/routerconsole/jsp/index.jsp index ecd0c68d3..c693c090c 100644 --- a/apps/routerconsole/jsp/index.jsp +++ b/apps/routerconsole/jsp/index.jsp @@ -19,7 +19,7 @@ if (System.getProperty("router.consoleNonce") == null) {
- <% File fpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getBaseDir(), "docs/news.xml"); %> + <% File fpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getRouterDir(), "docs/news.xml"); %> diff --git a/core/java/src/net/i2p/util/WorkingDir.java b/core/java/src/net/i2p/util/WorkingDir.java index a3fa6ca23..03ec21724 100644 --- a/core/java/src/net/i2p/util/WorkingDir.java +++ b/core/java/src/net/i2p/util/WorkingDir.java @@ -121,6 +121,7 @@ public class WorkingDir { // this one must be after MIGRATE_BASE success &= migrateJettyXml(oldDirf, dirf); success &= migrateClientsConfig(oldDirf, dirf); + success &= copy(new File(oldDirf, "docs/news.xml"), new File(dirf, "docs")); // Report success or failure if (success) { From 4d4954c5b820e91ee16dcc764615828913b713bd Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 14 Jun 2009 14:19:05 +0000 Subject: [PATCH 13/58] * ReseedHandler: - check for upper case HREF to be compatible with apache indexes --- .../java/src/net/i2p/router/web/ReseedHandler.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java index c11c6150c..944c4e910 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java @@ -137,11 +137,16 @@ public class ReseedHandler { * Fetch a directory listing and then up to 200 routerInfo files in the listing. * The listing must contain (exactly) strings that match: * href="routerInfo-{hash}.dat"> + * OR + * HREF="routerInfo-{hash}.dat"> * and then it fetches the files * {seedURL}routerInfo-{hash}.dat * after appending a '/' to seedURL if it doesn't have one. * Essentially this means that the seedURL must be a directory, it * can't end with 'index.html', for example. + * + * Jetty directory listings are not compatible, as they look like + * HREF="/full/path/to/routerInfo-... **/ private void reseedOne(String seedURL, boolean echoStatus) { @@ -165,8 +170,11 @@ public class ReseedHandler { int total = 0; while (total++ < 1000) { int start = content.indexOf("href=\"routerInfo-", cur); - if (start < 0) - break; + if (start < 0) { + start = content.indexOf("HREF=\"routerInfo-", cur); + if (start < 0) + break; + } int end = content.indexOf(".dat\">", start); String name = content.substring(start+"href=\"routerInfo-".length(), end); From 9b866b8e0692b0bd1c2daaac9cc224efdd05397a Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 14 Jun 2009 14:49:37 +0000 Subject: [PATCH 14/58] * I2PTunnel: - fix i2ptunnel.config save location --- .../java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java index a0b8ea3f7..88877ff4c 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java @@ -231,6 +231,8 @@ public class TunnelControllerGroup { public void saveConfig(String configFile) { _configFile = configFile; File cfgFile = new File(configFile); + if (!cfgFile.isAbsolute()) + cfgFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), configFile); File parent = cfgFile.getParentFile(); if ( (parent != null) && (!parent.exists()) ) parent.mkdirs(); From 3ee09df6cedd1a8b6711a07d7247a5574243e59e Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 15 Jun 2009 02:41:28 +0000 Subject: [PATCH 15/58] Treat 5.0.0.0/8 (Hamachi) as local --- router/java/src/net/i2p/router/transport/TransportImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java index 2dbd9af3a..e6e3c80e5 100644 --- a/router/java/src/net/i2p/router/transport/TransportImpl.java +++ b/router/java/src/net/i2p/router/transport/TransportImpl.java @@ -559,6 +559,7 @@ public abstract class TransportImpl implements Transport { if ((addr[0]&0xFF) >= 224) return false; // no multicast if ((addr[0]&0xFF) == 0) return false; if ( ((addr[0]&0xFF) == 169) && ((addr[1]&0xFF) == 254) ) return false; + if ((addr[0]&0xFF) == 5) return false; // Hamachi return true; // or at least possible to be true } else if (addr.length == 16) { return false; From 2ca0ae75294ab05ed74f56c2f5191747275213f6 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 15 Jun 2009 15:22:51 +0000 Subject: [PATCH 16/58] * i2psnark build: - Move FetchAndAdd to static inner class - Remove duplicate classes from i2psnark.war (120KB); fixes sporadic FetchAndAdd IllegalAccessError - Fix standalone build to include i2psnark.jar since classes aren't in the .war anymore - Have standalone jetty use I2PAppContext temp directory - Replace launch-i2psnark.jar with launch-i2psnark script, since RunStandalone is in i2p.jar - Clean up jetty-i2psnark.xml, turn off jetty logging - Remove standalone build from the pkg target in the main build.xml --- apps/i2psnark/java/build.xml | 28 ++------- .../org/klomp/snark/web/I2PSnarkServlet.java | 7 ++- .../org/klomp/snark/web/RunStandalone.java | 5 +- apps/i2psnark/jetty-i2psnark.xml | 63 +++---------------- apps/i2psnark/launch-i2psnark | 8 +++ apps/i2psnark/readme-standalone.txt | 6 +- build.xml | 5 +- 7 files changed, 36 insertions(+), 86 deletions(-) create mode 100755 apps/i2psnark/launch-i2psnark diff --git a/apps/i2psnark/java/build.xml b/apps/i2psnark/java/build.xml index d70233ac6..56ccb554d 100644 --- a/apps/i2psnark/java/build.xml +++ b/apps/i2psnark/java/build.xml @@ -37,16 +37,17 @@ - + + - + @@ -56,31 +57,13 @@ - - - - - - - - - - - - - - - - - - - + + @@ -92,7 +75,6 @@ - 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 410e804d9..3929fbe11 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -876,10 +876,9 @@ public class I2PSnarkServlet extends HttpServlet { private static final String TABLE_FOOTER = "\n"; private static final String FOOTER = ""; -} - -class FetchAndAdd implements Runnable { +/** inner class, don't bother reindenting */ +private static class FetchAndAdd implements Runnable { private SnarkManager _manager; private String _url; public FetchAndAdd(SnarkManager mgr, String url) { @@ -931,3 +930,5 @@ class FetchAndAdd implements Runnable { } } } + +} diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/RunStandalone.java b/apps/i2psnark/java/src/org/klomp/snark/web/RunStandalone.java index 05795b861..2880676a2 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/RunStandalone.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/RunStandalone.java @@ -2,6 +2,7 @@ package org.klomp.snark.web; import java.io.File; +import net.i2p.I2PAppContext; import net.i2p.util.FileUtil; import org.mortbay.jetty.Server; @@ -22,7 +23,7 @@ public class RunStandalone { } public void start() { - File workDir = new File("work"); + File workDir = new File(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work"); boolean workDirRemoved = FileUtil.rmdir(workDir, false); if (!workDirRemoved) System.err.println("ERROR: Unable to remove Jetty temporary work directory"); @@ -32,6 +33,8 @@ public class RunStandalone { try { _server = new Server("jetty-i2psnark.xml"); + // just blow up NPE if we don't have a context + (_server.getContexts()[0]).setTempDirectory(workDir); _server.start(); } catch (Exception e) { e.printStackTrace(); diff --git a/apps/i2psnark/jetty-i2psnark.xml b/apps/i2psnark/jetty-i2psnark.xml index 125db8b6d..0d4c56112 100644 --- a/apps/i2psnark/jetty-i2psnark.xml +++ b/apps/i2psnark/jetty-i2psnark.xml @@ -1,6 +1,12 @@ + + + + + + @@ -12,7 +18,7 @@ - + @@ -23,7 +29,7 @@ 8002 - 3 + 1 10 30000 1000 @@ -34,45 +40,6 @@ - - - - - - - - - - - - - @@ -91,20 +58,6 @@ webapps/i2psnark.war - - - - - - ./logs/yyyy_mm_dd.i2psnark-request.log - 90 - true - false - false - GMT - - - diff --git a/apps/i2psnark/launch-i2psnark b/apps/i2psnark/launch-i2psnark new file mode 100755 index 000000000..023b5a21a --- /dev/null +++ b/apps/i2psnark/launch-i2psnark @@ -0,0 +1,8 @@ +#!/bin/sh +# +# This launches i2psnark and jetty in a separate jvm. +# The file jetty-i2psnark.xml must be present in the current directory. +# i2psnark will be accessed at http://127.0.0.1:8002/ +# +I2P="." +java -cp "$I2P/lib/i2psnark.jar:$I2P/lib/i2p.jar:$I2P/lib/mstreaming.jar:$I2P/lib/streaming.jar:$I2P/lib/commons-el.jar:$I2P/lib/commons-logging.jar:$I2P/lib/jasper-compiler.jar:$I2P/lib/jasper-runtime.jar:$I2P/lib/javax.servlet.jar:$I2P/lib/org.mortbay.jetty.jar" org.klomp.snark.web.RunStandalone "$@" diff --git a/apps/i2psnark/readme-standalone.txt b/apps/i2psnark/readme-standalone.txt index 2f641db89..9bc1ddf66 100644 --- a/apps/i2psnark/readme-standalone.txt +++ b/apps/i2psnark/readme-standalone.txt @@ -1,6 +1,6 @@ To run I2PSnark from the command line, run "java -jar lib/i2psnark.jar", but -to run it with the web UI, run "java -jar launch-i2psnark.jar". I2PSnark is +to run it with the web UI, run "launch-i2psnark". I2PSnark is GPL'ed software, based on Snark (http://www.klomp.org/) to run on top of I2P -(http://www.i2p.net/) within a webserver (such as the bundled Jetty from +(http://www.i2p2.de/) within a webserver (such as the bundled Jetty from http://jetty.mortbay.org/). For more information about I2PSnark, get in touch -with the folks at http://forum.i2p.net/ \ No newline at end of file +with the folks at http://forum.i2p2.de/ diff --git a/build.xml b/build.xml index 78d549b33..376171dbb 100644 --- a/build.xml +++ b/build.xml @@ -39,7 +39,10 @@ + @@ -240,7 +243,7 @@ - + From 58660bed3c315fbc1a5dcc674a896222425437a4 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 15 Jun 2009 15:32:27 +0000 Subject: [PATCH 17/58] fix webapps path --- .../java/src/net/i2p/router/web/ConfigClientsHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 b612c005b..da79249ed 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java @@ -1,5 +1,6 @@ package net.i2p.router.web; +import java.io.File; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -111,7 +112,9 @@ public class ConfigClientsHandler extends FormHandler { for (int j = 0; j < hl.length; j++) { if (hl[j].getPort() == 7657) { try { - s.addWebApplication("/"+ app, "./webapps/" + app + ".war").start(); + File path = new File(_context.getBaseDir(), "webapps"); + path = new File(path, app + ".war"); + s.addWebApplication("/"+ app, path.getAbsolutePath()).start(); // no passwords... initialize(wac); addFormNotice("WebApp " + app + " started"); } catch (Exception ioe) { From bdd75793bc2127882f8b990e2fac1ff29b260bbc Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 15 Jun 2009 15:35:25 +0000 Subject: [PATCH 18/58] cleanup --- core/java/src/net/i2p/I2PAppContext.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index 1080ee6ac..025378d8c 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -177,12 +177,12 @@ public class I2PAppContext { * Base i2p.dir.base getBaseDir() lib/, webapps/, docs/, geoip/, licenses/, ... * Temp i2p.dir.temp getTempDir() Temporary files * Config i2p.dir.config getConfigDir() *.config, hosts.txt, addressbook/, ... - * PID i2p.dir.pid getPIDDir() wrapper *.pid files, router.ping + * PID i2p.dir.pid getPIDDir() router.ping * * (the following all default to the same as Config) * * Router i2p.dir.router getRouterDir() netDb/, peerProfiles/, router.*, keyBackup/, ... - * Log i2p.dir.log getLogDir() wrapper.log*, logs/ + * Log i2p.dir.log getLogDir() logs/ * App i2p.dir.app getAppDir() eepsite/, ... * * Note that we can't control where the wrapper puts its files. @@ -254,7 +254,7 @@ public class I2PAppContext { } else { _appDir = _routerDir; } - // comment these out later, don't want user names in the wrapper logs + /****** System.err.println("Base directory: " + _baseDir.getAbsolutePath()); System.err.println("Config directory: " + _configDir.getAbsolutePath()); System.err.println("Router directory: " + _routerDir.getAbsolutePath()); @@ -262,6 +262,7 @@ public class I2PAppContext { System.err.println("Log directory: " + _logDir.getAbsolutePath()); System.err.println("PID directory: " + _pidDir.getAbsolutePath()); System.err.println("Temp directory: " + getTempDir().getAbsolutePath()); + ******/ } public File getBaseDir() { return _baseDir; } @@ -276,7 +277,7 @@ public class I2PAppContext { if (_tmpDir == null) { String d = getProperty("i2p.dir.temp", System.getProperty("java.io.tmpdir")); // our random() probably isn't warmed up yet - String f = "i2p-" + (new java.util.Random()).nextInt() + ".tmp"; + String f = "i2p-" + Math.abs((new java.util.Random()).nextInt()) + ".tmp"; _tmpDir = new File(d, f); if (_tmpDir.exists()) { // good or bad ? From 279f3e4934b01efaf930ba94b792a75db94e2ca5 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 15 Jun 2009 21:20:52 +0000 Subject: [PATCH 19/58] dont make the new dir unless we are going to move there --- core/java/src/net/i2p/util/WorkingDir.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/java/src/net/i2p/util/WorkingDir.java b/core/java/src/net/i2p/util/WorkingDir.java index 03ec21724..227a0c3be 100644 --- a/core/java/src/net/i2p/util/WorkingDir.java +++ b/core/java/src/net/i2p/util/WorkingDir.java @@ -93,11 +93,6 @@ public class WorkingDir { System.err.println("Wanted to use " + rv + " for a working directory but it is not a directory"); return cwd; } - if (!dirf.mkdir()) { - System.err.println("Wanted to use " + rv + " for a working directory but could not create it"); - return cwd; - } - // Check for a router.keys file or logs dir, if either exists it's an old install, // and only migrate the data files if told to do so // (router.keys could be deleted later by a killkeys()) @@ -112,6 +107,11 @@ public class WorkingDir { return cwd; boolean migrateOldData = false; // this is a terrible idea + if (!dirf.mkdir()) { + System.err.println("Wanted to use " + rv + " for a working directory but could not create it"); + return cwd; + } + // Do the copying if (migrateOldData) System.err.println("Migrating data files to new user directory " + rv); From 7f379027cacbe875e95f84a49a5683e67839ee01 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 15 Jun 2009 21:27:38 +0000 Subject: [PATCH 20/58] * i2psnark build: - Put the duplicate classes back in the war, because the jar is not in the wrapper.config classpath in existing installs. We could take them out of the jar, but then they won't be available for standalone snark and future updates via snark. - Delete the dist/ dir in distclean --- apps/i2psnark/java/build.xml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/i2psnark/java/build.xml b/apps/i2psnark/java/build.xml index 56ccb554d..59f0421e7 100644 --- a/apps/i2psnark/java/build.xml +++ b/apps/i2psnark/java/build.xml @@ -44,10 +44,16 @@ - + - + @@ -86,6 +92,7 @@ + From 71f3cd648fa22bb9b80e761ed14c652d19a78043 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 15 Jun 2009 21:28:36 +0000 Subject: [PATCH 21/58] Fix wrapper.config issues on windows --- installer/install.xml | 2 +- installer/resources/wrapper.config | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/installer/install.xml b/installer/install.xml index 1abfda98d..1141b17ba 100644 --- a/installer/install.xml +++ b/installer/install.xml @@ -106,7 +106,7 @@ http://www.javalobby.org/forums/thread.jspa?threadID=15967&tstart=0 and the izpack docs for some guidance. --> - + diff --git a/installer/resources/wrapper.config b/installer/resources/wrapper.config index 03efe22fb..b9e446092 100644 --- a/installer/resources/wrapper.config +++ b/installer/resources/wrapper.config @@ -51,7 +51,8 @@ wrapper.java.additional.1=-DloggerFilenameOverride=logs/log-router-@.txt wrapper.java.additional.2=-Dorg.mortbay.http.Version.paranoid=true wrapper.java.additional.3=-Dorg.mortbay.util.FileResource.checkAliases=false wrapper.java.additional.4=-Dorg.mortbay.xml.XmlParser.NotValidating=true -wrapper.java.additional.5=-Di2p.dir.base=$INSTALL_PATH +wrapper.java.additional.5=-Di2p.dir.base="$INSTALL_PATH" +wrapper.java.additional.5.stripquotes=TRUE # Uncomment this for better performance. # If it doesn't work, server mode is not available in your JVM. # This may not be required if your machine is already "server-class". From 7aa9949332ca0d390bef8f2bba2a630ad32d99ea Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 15 Jun 2009 21:58:28 +0000 Subject: [PATCH 22/58] * Reseeding / NetDb: - Move reseeding from the routerconsole app to the router, so that we can bootstrap an embedded router lacking a routerconsole (iMule or android for example), without additional modifications. This allows better integration between the reseeding function and the netDb. - Call reseed from PersistentDataStore, not from the routerconsole init, and start seeding as soon as the netdb has read the netDb/ directory, not when the console starts. - Wake up the netdb reader as soon as reseeding is done, rather than waiting up to 60s. - Don't display the reseed button on the console until the netdb initialization is done. * NetDb: - Fix an NPE on early shutdown * RouterConsoleRunner: - Catch a class not found error better --- .../src/net/i2p/router/web/ReseedHandler.java | 266 +----------------- .../i2p/router/web/RouterConsoleRunner.java | 22 +- .../src/net/i2p/router/web/SummaryHelper.java | 5 +- .../net/i2p/router/NetworkDatabaseFacade.java | 2 + .../router/networkdb/kademlia/DataStore.java | 2 + .../KademliaNetworkDatabaseFacade.java | 14 +- .../kademlia/PersistentDataStore.java | 25 +- .../kademlia/TransientDataStore.java | 4 + .../networkdb/reseed/ReseedChecker.java | 46 +++ .../i2p/router/networkdb/reseed/Reseeder.java | 266 ++++++++++++++++++ 10 files changed, 365 insertions(+), 287 deletions(-) create mode 100644 router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java create mode 100644 router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java index 944c4e910..31c5efc41 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java @@ -1,63 +1,21 @@ package net.i2p.router.web; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.StringTokenizer; - -import net.i2p.I2PAppContext; import net.i2p.router.RouterContext; -import net.i2p.util.EepGet; -import net.i2p.util.I2PThread; -import net.i2p.util.Log; +import net.i2p.router.networkdb.reseed.Reseeder; /** - * Handler to deal with reseed requests. This will reseed from the URL - * http://i2pdb.tin0.de/netDb/ unless the I2P configuration property "i2p.reseedURL" is - * set. It always writes to ./netDb/, so don't mess with that. - * + * Handler to deal with reseed requests. */ -public class ReseedHandler { - private static ReseedRunner _reseedRunner; - private RouterContext _context; - private Log _log; - - // Reject unreasonably big files, because we download into a ByteArrayOutputStream. - private static final long MAX_RESEED_RESPONSE_SIZE = 8 * 1024 * 1024; - - private static final String DEFAULT_SEED_URL = "http://i2pdb.tin0.de/netDb/,http://netdb.i2p2.de/"; +public class ReseedHandler extends HelperBase { + private static Reseeder _reseedRunner; public ReseedHandler() { this(ContextHelper.getContext(null)); } public ReseedHandler(RouterContext ctx) { _context = ctx; - _log = ctx.logManager().getLog(ReseedHandler.class); } - /** - * Configure this bean to query a particular router context - * - * @param contextId begging few characters of the routerHash, or null to pick - * the first one we come across. - */ - public void setContextId(String contextId) { - try { - _context = ContextHelper.getContext(contextId); - _log = _context.logManager().getLog(ReseedHandler.class); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - public void setReseedNonce(String nonce) { if (nonce == null) return; if (nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.nonce")) || @@ -69,220 +27,8 @@ public class ReseedHandler { public void requestReseed() { synchronized (ReseedHandler.class) { if (_reseedRunner == null) - _reseedRunner = new ReseedRunner(); - if (_reseedRunner.isRunning()) { - return; - } else { - System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "true"); - I2PThread reseed = new I2PThread(_reseedRunner, "Reseed"); - reseed.start(); - } + _reseedRunner = new Reseeder(_context); + _reseedRunner.requestReseed(); } - } - - public class ReseedRunner implements Runnable, EepGet.StatusListener { - private boolean _isRunning; - - public ReseedRunner() { - _isRunning = false; - System.setProperty("net.i2p.router.web.ReseedHandler.statusMessage","Reseeding."); - } - public boolean isRunning() { return _isRunning; } - public void run() { - _isRunning = true; - System.out.println("Reseed start"); - reseed(false); - System.out.println("Reseed complete"); - System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"); - _isRunning = false; - } - - // EepGet status listeners - public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) { - // Since readURL() runs an EepGet with 0 retries, - // we can report errors with attemptFailed() instead of transferFailed(). - // It has the benefit of providing cause of failure, which helps resolve issues. - if (_log.shouldLog(Log.ERROR)) _log.error("EepGet failed on " + url, cause); - } - public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {} - public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {} - public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {} - public void headerReceived(String url, int attemptNum, String key, String val) {} - public void attempting(String url) {} - // End of EepGet status listeners - - /** - * Reseed has been requested, so lets go ahead and do it. Fetch all of - * the routerInfo-*.dat files from the specified URL (or the default) and - * save them into this router's netDb dir. - * - */ - private static final String RESEED_TIPS = - "Ensure that nothing blocks outbound HTTP, check logs " + - "and if nothing helps, read FAQ about reseeding manually."; - - private void reseed(boolean echoStatus) { - List URLList = new ArrayList(); - String URLs = _context.getProperty("i2p.reseedURL", DEFAULT_SEED_URL); - StringTokenizer tok = new StringTokenizer(URLs, " ,"); - while (tok.hasMoreTokens()) - URLList.add(tok.nextToken().trim()); - Collections.shuffle(URLList); - for (int i = 0; i < URLList.size() && _isRunning; i++) - reseedOne((String) URLList.get(i), echoStatus); - } - - /** - * Fetch a directory listing and then up to 200 routerInfo files in the listing. - * The listing must contain (exactly) strings that match: - * href="routerInfo-{hash}.dat"> - * OR - * HREF="routerInfo-{hash}.dat"> - * and then it fetches the files - * {seedURL}routerInfo-{hash}.dat - * after appending a '/' to seedURL if it doesn't have one. - * Essentially this means that the seedURL must be a directory, it - * can't end with 'index.html', for example. - * - * Jetty directory listings are not compatible, as they look like - * HREF="/full/path/to/routerInfo-... - **/ - private void reseedOne(String seedURL, boolean echoStatus) { - - try { - System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage",""); - System.setProperty("net.i2p.router.web.ReseedHandler.statusMessage","Reseeding: fetching seed URL."); - System.err.println("Reseed from " + seedURL); - URL dir = new URL(seedURL); - byte contentRaw[] = readURL(dir); - if (contentRaw == null) { - System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage", - "Last reseed failed fully (failed reading seed URL). " + - RESEED_TIPS); - // Logging deprecated here since attemptFailed() provides better info - _log.debug("Failed reading seed URL: " + seedURL); - return; - } - String content = new String(contentRaw); - Set urls = new HashSet(); - int cur = 0; - int total = 0; - while (total++ < 1000) { - int start = content.indexOf("href=\"routerInfo-", cur); - if (start < 0) { - start = content.indexOf("HREF=\"routerInfo-", cur); - if (start < 0) - break; - } - - int end = content.indexOf(".dat\">", start); - String name = content.substring(start+"href=\"routerInfo-".length(), end); - urls.add(name); - cur = end + 1; - } - if (total <= 0) { - _log.error("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs."); - System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage", - "Last reseed failed fully (no routerInfo URLs at seed URL). " + - RESEED_TIPS); - return; - } - - List urlList = new ArrayList(urls); - Collections.shuffle(urlList); - int fetched = 0; - int errors = 0; - // 200 max from one URL - for (Iterator iter = urlList.iterator(); iter.hasNext() && fetched < 200; ) { - try { - System.setProperty("net.i2p.router.web.ReseedHandler.statusMessage", - "Reseeding: fetching router info from seed URL (" + - fetched + " successful, " + errors + " errors, " + total + " total)."); - - fetchSeed(seedURL, (String)iter.next()); - fetched++; - if (echoStatus) { - System.out.print("."); - if (fetched % 60 == 0) - System.out.println(); - } - } catch (Exception e) { - errors++; - } - } - System.err.println("Reseed got " + fetched + " router infos from " + seedURL); - - int failPercent = 100 * errors / total; - - // Less than 10% of failures is considered success, - // because some routerInfos will always fail. - if ((failPercent >= 10) && (failPercent < 90)) { - System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage", - "Last reseed failed partly (" + failPercent + "% of " + total + "). " + - RESEED_TIPS); - } - if (failPercent >= 90) { - System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage", - "Last reseed failed (" + failPercent + "% of " + total + "). " + - RESEED_TIPS); - } - // Don't go on to the next URL if we have enough - if (fetched >= 100) - _isRunning = false; - } catch (Throwable t) { - System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage", - "Last reseed failed fully (exception caught). " + - RESEED_TIPS); - _log.error("Error reseeding", t); - } - } - - /* Since we don't return a value, we should always throw an exception if something fails. */ - private void fetchSeed(String seedURL, String peer) throws Exception { - URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat"); - - byte data[] = readURL(url); - if (data == null) { - // Logging deprecated here since attemptFailed() provides better info - _log.debug("Failed fetching seed: " + url.toString()); - throw new Exception ("Failed fetching seed."); - } - //System.out.println("read: " + (data != null ? data.length : -1)); - writeSeed(peer, data); - } - - private byte[] readURL(URL url) throws Exception { - ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024); - - // Do a non-proxied eepget into our ByteArrayOutputStream with 0 retries - EepGet get = new EepGet( I2PAppContext.getGlobalContext(), false, null, -1, 0, 0, MAX_RESEED_RESPONSE_SIZE, - null, baos, url.toString(), false, null, null); - get.addStatusListener(ReseedRunner.this); - if (get.fetch()) return baos.toByteArray(); else return null; - } - - private void writeSeed(String name, byte data[]) throws Exception { - String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb"); - File netDbDir = new File(_context.getRouterDir(), dirName); - if (!netDbDir.exists()) { - boolean ok = netDbDir.mkdirs(); - } - FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat")); - fos.write(data); - fos.close(); - } - - } - - public static void main(String args[]) { - if ( (args != null) && (args.length == 1) && (!Boolean.valueOf(args[0]).booleanValue()) ) { - System.out.println("Not reseeding, as requested"); - return; // not reseeding on request - } - System.out.println("Reseeding"); - ReseedHandler reseedHandler = new ReseedHandler(); - reseedHandler.requestReseed(); - } - } 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 25b990f04..bbf9852c3 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -144,7 +144,8 @@ public class RouterConsoleRunner { storeWebAppProperties(props); try { _server.start(); - } catch (Exception me) { + } catch (Throwable me) { + // NoClassFoundDefError from a webapp is a throwable, not an exception System.err.println("WARNING: Error starting one or more listeners of the Router Console server.\n" + "If your console is still accessible at http://127.0.0.1:7657/,\n" + "this may be a problem only with binding to the IPV6 address ::1.\n" + @@ -158,25 +159,6 @@ public class RouterConsoleRunner { t.printStackTrace(); } - // we check the i2p installation directory (.) for a flag telling us not to reseed, - // but also check the home directory for that flag too, since new users installing i2p - // don't have an installation directory that they can put the flag in yet. - File noReseedFile = new File(new File(System.getProperty("user.home")), ".i2pnoreseed"); - File noReseedFileAlt1 = new File(new File(System.getProperty("user.home")), "noreseed.i2p"); - File noReseedFileAlt2 = new File(I2PAppContext.getGlobalContext().getConfigDir(), ".i2pnoreseed"); - File noReseedFileAlt3 = new File(I2PAppContext.getGlobalContext().getConfigDir(), "noreseed.i2p"); - if (!noReseedFile.exists() && !noReseedFileAlt1.exists() && !noReseedFileAlt2.exists() && !noReseedFileAlt3.exists()) { - File netDb = new File(I2PAppContext.getGlobalContext().getRouterDir(), "netDb"); - // sure, some of them could be "my.info" or various leaseSet- files, but chances are, - // if someone has those files, they've already been seeded (at least enough to let them - // get i2p started - they can reseed later in the web console) - String names[] = (netDb.exists() ? netDb.list() : null); - if ( (names == null) || (names.length < 15) ) { - ReseedHandler reseedHandler = new ReseedHandler(); - reseedHandler.requestReseed(); - } - } - NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext()); I2PThread t = new I2PThread(fetcher, "NewsFetcher"); t.setDaemon(true); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java index 0810dd950..66e9d9f25 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java @@ -85,8 +85,9 @@ public class SummaryHelper extends HelperBase { } public boolean allowReseed() { - return (_context.netDb().getKnownRouters() < 30) || - Boolean.valueOf(_context.getProperty("i2p.alwaysAllowReseed", "false")).booleanValue(); + return _context.netDb().isInitialized() && + ((_context.netDb().getKnownRouters() < 30) || + Boolean.valueOf(_context.getProperty("i2p.alwaysAllowReseed")).booleanValue()); } public int getAllPeers() { return _context.netDb().getKnownRouters(); } diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java index 1ff7af132..865fbf8ec 100644 --- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java @@ -58,6 +58,8 @@ public abstract class NetworkDatabaseFacade implements Service { public abstract Set getAllRouters(); public int getKnownRouters() { return 0; } public int getKnownLeaseSets() { return 0; } + public boolean isInitialized() { return true; } + public void rescan() {} public void renderRouterInfoHTML(Writer out, String s) throws IOException {} public void renderLeaseSetHTML(Writer out) throws IOException {} public void renderStatusHTML(Writer out, boolean b) throws IOException {} diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java index 75329f896..ae4132678 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java @@ -14,6 +14,7 @@ import net.i2p.data.DataStructure; import net.i2p.data.Hash; public interface DataStore { + public boolean isInitialized(); public boolean isKnown(Hash key); public DataStructure get(Hash key); public DataStructure get(Hash key, boolean persist); @@ -24,6 +25,7 @@ public interface DataStore { public Set getKeys(); public void stop(); public void restart(); + public void rescan(); public int countLeaseSets(); } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index 31d6dc04b..ae5dd9d80 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -139,6 +139,11 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { context.statManager().createRateStat("netDb.exploreKeySet", "how many keys are queued for exploration?", "NetworkDatabase", new long[] { 10*60*1000 }); } + @Override + public boolean isInitialized() { + return _initialized && _ds != null && _ds.isInitialized(); + } + protected PeerSelector createPeerSelector() { return new PeerSelector(_context); } public PeerSelector getPeerSelector() { return _peerSelector; } @@ -177,7 +182,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public void shutdown() { _initialized = false; _kb = null; - _ds.stop(); + if (_ds != null) + _ds.stop(); _ds = null; _exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't. // _exploreKeys = null; @@ -203,6 +209,12 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { publish(ri); } + @Override + public void rescan() { + if (isInitialized()) + _ds.rescan(); + } + String getDbDir() { return _dbDir; } public void startup() { diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java index 92124d690..d80a3248b 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java @@ -26,6 +26,7 @@ import net.i2p.data.RouterInfo; import net.i2p.router.JobImpl; import net.i2p.router.Router; import net.i2p.router.RouterContext; +import net.i2p.router.networkdb.reseed.ReseedChecker; import net.i2p.util.I2PThread; import net.i2p.util.Log; @@ -39,6 +40,8 @@ class PersistentDataStore extends TransientDataStore { private String _dbDir; private KademliaNetworkDatabaseFacade _facade; private Writer _writer; + private ReadJob _readJob; + private boolean _initialized; private final static int READ_DELAY = 60*1000; @@ -47,7 +50,8 @@ class PersistentDataStore extends TransientDataStore { _log = ctx.logManager().getLog(PersistentDataStore.class); _dbDir = dbDir; _facade = facade; - _context.jobQueue().addJob(new ReadJob()); + _readJob = new ReadJob(); + _context.jobQueue().addJob(_readJob); ctx.statManager().createRateStat("netDb.writeClobber", "How often we clobber a pending netDb write", "NetworkDatabase", new long[] { 20*60*1000 }); ctx.statManager().createRateStat("netDb.writePending", "How many pending writes are there", "NetworkDatabase", new long[] { 60*1000 }); ctx.statManager().createRateStat("netDb.writeOut", "How many we wrote", "NetworkDatabase", new long[] { 20*60*1000 }); @@ -58,7 +62,10 @@ class PersistentDataStore extends TransientDataStore { //writer.setDaemon(true); writer.start(); } - + + public boolean isInitialized() { return _initialized; } + + // this doesn't stop the read job or the writer, maybe it should? @Override public void stop() { super.stop(); @@ -71,6 +78,11 @@ class PersistentDataStore extends TransientDataStore { _dbDir = _facade.getDbDir(); } + public void rescan() { + if (_initialized) + _readJob.wakeup(); + } + @Override public DataStructure get(Hash key) { return get(key, true); @@ -317,6 +329,10 @@ class PersistentDataStore extends TransientDataStore { requeue(READ_DELAY); } + public void wakeup() { + requeue(0); + } + private void readFiles() { int routerCount = 0; try { @@ -336,9 +352,10 @@ class PersistentDataStore extends TransientDataStore { _log.error("Error reading files in the db dir", ioe); } - if ( (routerCount <= 5) && (!_alreadyWarned) ) { - _log.error("Very few routerInfo files remaining - please reseed"); + if (!_alreadyWarned) { + ReseedChecker.checkReseed(_context, routerCount); _alreadyWarned = true; + _initialized = true; } } } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java index 5028b5ea1..ace0a9666 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java @@ -35,6 +35,8 @@ class TransientDataStore implements DataStore { _log.info("Data Store initialized"); } + public boolean isInitialized() { return true; } + public void stop() { _data.clear(); } @@ -43,6 +45,8 @@ class TransientDataStore implements DataStore { stop(); } + public void rescan() {} + public Set getKeys() { return new HashSet(_data.keySet()); } diff --git a/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java b/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java new file mode 100644 index 000000000..aa38be5e7 --- /dev/null +++ b/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java @@ -0,0 +1,46 @@ +package net.i2p.router.networkdb.reseed; + +import java.io.File; + +import net.i2p.router.RouterContext; +import net.i2p.util.Log; + +/** + * Moved from RouterConsoleRunner.java + * + * Reseeding is not strictly a router function, it used to be + * in the routerconsole app, but this made it impossible to + * bootstrap an embedded router lacking a routerconsole, + * in iMule or android for example, without additional modifications. + * + * Also, as this is now called from PersistentDataStore, not from the + * routerconsole, we can get started as soon as the netdb has read + * the netDb/ directory, not when the console starts, +router/java/src/net/i2p/router/networkdb/eseed/ReseedChecker.java + */ +public class ReseedChecker { + + private static final int MINIMUM = 15; + + public static void checkReseed(RouterContext context, int count) { + if (count >= MINIMUM) + return; + + // we check the i2p installation directory for a flag telling us not to reseed, + // but also check the home directory for that flag too, since new users installing i2p + // don't have an installation directory that they can put the flag in yet. + File noReseedFile = new File(new File(System.getProperty("user.home")), ".i2pnoreseed"); + File noReseedFileAlt1 = new File(new File(System.getProperty("user.home")), "noreseed.i2p"); + File noReseedFileAlt2 = new File(context.getConfigDir(), ".i2pnoreseed"); + File noReseedFileAlt3 = new File(context.getConfigDir(), "noreseed.i2p"); + if (!noReseedFile.exists() && !noReseedFileAlt1.exists() && !noReseedFileAlt2.exists() && !noReseedFileAlt3.exists()) { + Log _log = context.logManager().getLog(ReseedChecker.class); + if (count <= 1) + _log.error("Downloading peer router information for a new I2P installation"); + else + _log.error("Very few routerInfo files remaining - reseeding now"); + Reseeder reseeder = new Reseeder(context); + reseeder.requestReseed(); + } + } +} diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java new file mode 100644 index 000000000..bcecf0753 --- /dev/null +++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java @@ -0,0 +1,266 @@ +package net.i2p.router.networkdb.reseed; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; + +import net.i2p.I2PAppContext; +import net.i2p.router.RouterContext; +import net.i2p.util.EepGet; +import net.i2p.util.I2PThread; +import net.i2p.util.Log; + +/** + * Moved from ReseedHandler in routerconsole. See ReseedChecker for additional comments. + * + * Handler to deal with reseed requests. This will reseed from the URLs + * specified below unless the I2P configuration property "i2p.reseedURL" is + * set. It always writes to ./netDb/, so don't mess with that. + * + */ +public class Reseeder { + private static ReseedRunner _reseedRunner; + private RouterContext _context; + private Log _log; + + // Reject unreasonably big files, because we download into a ByteArrayOutputStream. + private static final long MAX_RESEED_RESPONSE_SIZE = 8 * 1024 * 1024; + + private static final String DEFAULT_SEED_URL = "http://i2pdb.tin0.de/netDb/,http://netdb.i2p2.de/"; + + public Reseeder(RouterContext ctx) { + _context = ctx; + _log = ctx.logManager().getLog(Reseeder.class); + } + + public void requestReseed() { + synchronized (Reseeder.class) { + if (_reseedRunner == null) + _reseedRunner = new ReseedRunner(); + if (_reseedRunner.isRunning()) { + return; + } else { + System.setProperty("net.i2p.router.web.Reseeder.reseedInProgress", "true"); + I2PThread reseed = new I2PThread(_reseedRunner, "Reseed"); + reseed.start(); + } + } + + } + + public class ReseedRunner implements Runnable, EepGet.StatusListener { + private boolean _isRunning; + + public ReseedRunner() { + _isRunning = false; + System.setProperty("net.i2p.router.web.Reseeder.statusMessage","Reseeding."); + } + public boolean isRunning() { return _isRunning; } + public void run() { + _isRunning = true; + System.out.println("Reseed start"); + reseed(false); + System.out.println("Reseed complete"); + System.setProperty("net.i2p.router.web.Reseeder.reseedInProgress", "false"); + _isRunning = false; + } + + // EepGet status listeners + public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) { + // Since readURL() runs an EepGet with 0 retries, + // we can report errors with attemptFailed() instead of transferFailed(). + // It has the benefit of providing cause of failure, which helps resolve issues. + if (_log.shouldLog(Log.ERROR)) _log.error("EepGet failed on " + url, cause); + } + public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {} + public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {} + public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {} + public void headerReceived(String url, int attemptNum, String key, String val) {} + public void attempting(String url) {} + // End of EepGet status listeners + + /** + * Reseed has been requested, so lets go ahead and do it. Fetch all of + * the routerInfo-*.dat files from the specified URL (or the default) and + * save them into this router's netDb dir. + * + */ + private static final String RESEED_TIPS = + "Ensure that nothing blocks outbound HTTP, check logs " + + "and if nothing helps, read FAQ about reseeding manually."; + + private void reseed(boolean echoStatus) { + List URLList = new ArrayList(); + String URLs = _context.getProperty("i2p.reseedURL", DEFAULT_SEED_URL); + StringTokenizer tok = new StringTokenizer(URLs, " ,"); + while (tok.hasMoreTokens()) + URLList.add(tok.nextToken().trim()); + Collections.shuffle(URLList); + for (int i = 0; i < URLList.size() && _isRunning; i++) + reseedOne((String) URLList.get(i), echoStatus); + } + + /** + * Fetch a directory listing and then up to 200 routerInfo files in the listing. + * The listing must contain (exactly) strings that match: + * href="routerInfo-{hash}.dat"> + * OR + * HREF="routerInfo-{hash}.dat"> + * and then it fetches the files + * {seedURL}routerInfo-{hash}.dat + * after appending a '/' to seedURL if it doesn't have one. + * Essentially this means that the seedURL must be a directory, it + * can't end with 'index.html', for example. + * + * Jetty directory listings are not compatible, as they look like + * HREF="/full/path/to/routerInfo-... + **/ + private void reseedOne(String seedURL, boolean echoStatus) { + + try { + System.setProperty("net.i2p.router.web.Reseeder.errorMessage",""); + System.setProperty("net.i2p.router.web.Reseeder.statusMessage","Reseeding: fetching seed URL."); + System.err.println("Reseed from " + seedURL); + URL dir = new URL(seedURL); + byte contentRaw[] = readURL(dir); + if (contentRaw == null) { + System.setProperty("net.i2p.router.web.Reseeder.errorMessage", + "Last reseed failed fully (failed reading seed URL). " + + RESEED_TIPS); + // Logging deprecated here since attemptFailed() provides better info + _log.debug("Failed reading seed URL: " + seedURL); + return; + } + String content = new String(contentRaw); + Set urls = new HashSet(); + int cur = 0; + int total = 0; + while (total++ < 1000) { + int start = content.indexOf("href=\"routerInfo-", cur); + if (start < 0) { + start = content.indexOf("HREF=\"routerInfo-", cur); + if (start < 0) + break; + } + + int end = content.indexOf(".dat\">", start); + String name = content.substring(start+"href=\"routerInfo-".length(), end); + urls.add(name); + cur = end + 1; + } + if (total <= 0) { + _log.error("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs."); + System.setProperty("net.i2p.router.web.Reseeder.errorMessage", + "Last reseed failed fully (no routerInfo URLs at seed URL). " + + RESEED_TIPS); + return; + } + + List urlList = new ArrayList(urls); + Collections.shuffle(urlList); + int fetched = 0; + int errors = 0; + // 200 max from one URL + for (Iterator iter = urlList.iterator(); iter.hasNext() && fetched < 200; ) { + try { + System.setProperty("net.i2p.router.web.Reseeder.statusMessage", + "Reseeding: fetching router info from seed URL (" + + fetched + " successful, " + errors + " errors, " + total + " total)."); + + fetchSeed(seedURL, (String)iter.next()); + fetched++; + if (echoStatus) { + System.out.print("."); + if (fetched % 60 == 0) + System.out.println(); + } + } catch (Exception e) { + errors++; + } + } + System.err.println("Reseed got " + fetched + " router infos from " + seedURL); + + int failPercent = 100 * errors / total; + + // Less than 10% of failures is considered success, + // because some routerInfos will always fail. + if ((failPercent >= 10) && (failPercent < 90)) { + System.setProperty("net.i2p.router.web.Reseeder.errorMessage", + "Last reseed failed partly (" + failPercent + "% of " + total + "). " + + RESEED_TIPS); + } + if (failPercent >= 90) { + System.setProperty("net.i2p.router.web.Reseeder.errorMessage", + "Last reseed failed (" + failPercent + "% of " + total + "). " + + RESEED_TIPS); + } + if (fetched > 0) + _context.netDb().rescan(); + // Don't go on to the next URL if we have enough + if (fetched >= 100) + _isRunning = false; + } catch (Throwable t) { + System.setProperty("net.i2p.router.web.Reseeder.errorMessage", + "Last reseed failed fully (exception caught). " + + RESEED_TIPS); + _log.error("Error reseeding", t); + } + } + + /* Since we don't return a value, we should always throw an exception if something fails. */ + private void fetchSeed(String seedURL, String peer) throws Exception { + URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat"); + + byte data[] = readURL(url); + if (data == null) { + // Logging deprecated here since attemptFailed() provides better info + _log.debug("Failed fetching seed: " + url.toString()); + throw new Exception ("Failed fetching seed."); + } + //System.out.println("read: " + (data != null ? data.length : -1)); + writeSeed(peer, data); + } + + private byte[] readURL(URL url) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024); + + // Do a non-proxied eepget into our ByteArrayOutputStream with 0 retries + EepGet get = new EepGet( I2PAppContext.getGlobalContext(), false, null, -1, 0, 0, MAX_RESEED_RESPONSE_SIZE, + null, baos, url.toString(), false, null, null); + get.addStatusListener(ReseedRunner.this); + if (get.fetch()) return baos.toByteArray(); else return null; + } + + private void writeSeed(String name, byte data[]) throws Exception { + String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb"); + File netDbDir = new File(_context.getRouterDir(), dirName); + if (!netDbDir.exists()) { + boolean ok = netDbDir.mkdirs(); + } + FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat")); + fos.write(data); + fos.close(); + } + + } + +/****** + public static void main(String args[]) { + if ( (args != null) && (args.length == 1) && (!Boolean.valueOf(args[0]).booleanValue()) ) { + System.out.println("Not reseeding, as requested"); + return; // not reseeding on request + } + System.out.println("Reseeding"); + Reseeder reseedHandler = new Reseeder(); + reseedHandler.requestReseed(); + } +******/ +} From a1ec01ec2ddc0f70e0ff36fefc6015d5ffb6ffaa Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 16 Jun 2009 17:38:23 +0000 Subject: [PATCH 23/58] * Browser Launch: Wait until the routerconsole is up before launching the browser --- .../src/net/i2p/apps/systray/UrlLauncher.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java index 55a7b5277..b0f6425d3 100644 --- a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java +++ b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java @@ -12,9 +12,12 @@ package net.i2p.apps.systray; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.net.InetSocketAddress; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; +import java.net.Socket; +import java.net.SocketAddress; import java.net.URL; import net.i2p.I2PAppContext; @@ -34,6 +37,56 @@ public class UrlLauncher { ShellCommand _shellCommand = new ShellCommand(); + private static final int WAIT_TIME = 5*1000; + private static final int MAX_WAIT_TIME = 5*60*1000; + private static final int MAX_TRIES = 99; + + /** + * Prevent bad user experience by waiting for the server to be there + * before launching the browser. + * @return success + */ + public boolean waitForServer(String urlString) { + URL url; + try { + url = new URL(urlString); + } catch (MalformedURLException e) { + return false; + } + String host = url.getHost(); + int port = url.getPort(); + if (port <= 0) { + port = url.getDefaultPort(); + if (port <= 0) + return false; + } + SocketAddress sa; + try { + sa = new InetSocketAddress(host, port); + } catch (IllegalArgumentException iae) { + return false; + } + long done = System.currentTimeMillis() + MAX_WAIT_TIME; + for (int i = 0; i < MAX_TRIES; i++) { + try { + Socket test = new Socket(); + // this will usually fail right away if it's going to fail since it's local + test.connect(sa, WAIT_TIME); + // it worked + try { + test.close(); + } catch (IOException ioe) {} + return true; + } catch (Exception e) {} + if (System.currentTimeMillis() > done) + break; + try { + Thread.sleep(WAIT_TIME); + } catch (InterruptedException ie) {} + } + return false; + } + /** * Discovers the operating system the installer is running under and tries * to launch the given URL using the default browser for that platform; if @@ -50,6 +103,7 @@ public class UrlLauncher { String osName = System.getProperty("os.name"); + waitForServer(url); if (validateUrlFormat(url)) { if (osName.toLowerCase().indexOf("mac") > -1) { if (osName.toLowerCase().startsWith("mac os x")) { @@ -143,6 +197,7 @@ public class UrlLauncher { */ public boolean openUrl(String url, String browser) throws Exception { + waitForServer(url); if (validateUrlFormat(url)) if (_shellCommand.executeSilentAndWaitTimed(browser + " " + url, 5)) return true; From 978de733514afb5cb463e017f3fd78112e293f29 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 16 Jun 2009 18:01:43 +0000 Subject: [PATCH 24/58] * netdb.jsp: Add country chart at bottom, clean up version chart --- core/java/src/net/i2p/util/ObjectCounter.java | 42 ++++++++++++ .../src/net/i2p/router/CommSystemFacade.java | 1 + .../KademliaNetworkDatabaseFacade.java | 67 +++++++++++-------- .../transport/CommSystemFacadeImpl.java | 16 +++-- .../router/tunnel/pool/TunnelPoolManager.java | 38 +++-------- 5 files changed, 102 insertions(+), 62 deletions(-) create mode 100644 core/java/src/net/i2p/util/ObjectCounter.java diff --git a/core/java/src/net/i2p/util/ObjectCounter.java b/core/java/src/net/i2p/util/ObjectCounter.java new file mode 100644 index 000000000..19c92ae66 --- /dev/null +++ b/core/java/src/net/i2p/util/ObjectCounter.java @@ -0,0 +1,42 @@ +package net.i2p.util; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Count things. + * + * @author zzz + */ +public class ObjectCounter { + private ConcurrentHashMap _map; + public ObjectCounter() { + _map = new ConcurrentHashMap(); + } + /** + * Add one. + * Not perfectly concurrent, new AtomicInteger(1) would be better, + * at the cost of some object churn. + */ + public void increment(K h) { + Integer i = _map.putIfAbsent(h, Integer.valueOf(1)); + if (i != null) + _map.put(h, Integer.valueOf(i.intValue() + 1)); + } + /** + * @return current count + */ + public int count(K h) { + Integer i = _map.get(h); + if (i != null) + return i.intValue(); + return 0; + } + /** + * @return set of objects with counts > 0 + */ + public Set objects() { + return _map.keySet(); + } +} + diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java index 2e85a7eb5..4fe9147ac 100644 --- a/router/java/src/net/i2p/router/CommSystemFacade.java +++ b/router/java/src/net/i2p/router/CommSystemFacade.java @@ -62,6 +62,7 @@ public abstract class CommSystemFacade implements Service { public byte[] getIP(Hash dest) { return null; } public void queueLookup(byte[] ip) {} public String getCountry(Hash peer) { return null; } + public String getCountryName(String code) { return code; } public String renderPeerHTML(Hash peer) { return null; } /** diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index ae5dd9d80..e61a58554 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -10,12 +10,15 @@ package net.i2p.router.networkdb.kademlia; import java.io.IOException; import java.io.Writer; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -42,6 +45,7 @@ import net.i2p.router.networkdb.DatabaseStoreMessageHandler; import net.i2p.router.networkdb.PublishLocalRouterInfoJob; import net.i2p.router.peermanager.PeerProfile; import net.i2p.util.Log; +import net.i2p.util.ObjectCounter; /** * Kademlia based version of the network database @@ -1011,8 +1015,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { out.write(buf.toString()); buf.setLength(0); - /* coreVersion to Map of routerVersion to Integer */ - Map versions = new TreeMap(); + ObjectCounter versions = new ObjectCounter(); + ObjectCounter countries = new ObjectCounter(); Set routers = new TreeSet(new RouterInfoComparator()); routers.addAll(getRouters()); @@ -1024,40 +1028,47 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { renderRouterInfo(buf, ri, false, full); out.write(buf.toString()); buf.setLength(0); - String coreVersion = ri.getOption("coreVersion"); String routerVersion = ri.getOption("router.version"); - if ( (coreVersion != null) && (routerVersion != null) ) { - Map routerVersions = (Map)versions.get(coreVersion); - if (routerVersions == null) { - routerVersions = new TreeMap(); - versions.put(coreVersion, routerVersions); - } - Integer val = (Integer)routerVersions.get(routerVersion); - if (val == null) - routerVersions.put(routerVersion, Integer.valueOf(1)); - else - routerVersions.put(routerVersion, Integer.valueOf(val.intValue() + 1)); - } + if (routerVersion != null) + versions.increment(routerVersion); + String country = _context.commSystem().getCountry(key); + if(country != null) + countries.increment(country); } } - if (versions.size() > 0) { + buf.append("
"); + List versionList = new ArrayList(versions.objects()); + if (versionList.size() > 0) { + Collections.sort(versionList, Collections.reverseOrder()); buf.append("\n"); - buf.append("\n"); - for (Iterator iter = versions.entrySet().iterator(); iter.hasNext(); ) { - Map.Entry entry = (Map.Entry)iter.next(); - String coreVersion = (String)entry.getKey(); - Map routerVersions = (Map)entry.getValue(); - for (Iterator routerIter = routerVersions.keySet().iterator(); routerIter.hasNext(); ) { - String routerVersion = (String)routerIter.next(); - Integer num = (Integer)routerVersions.get(routerVersion); - buf.append("\n"); - } + buf.append("\n"); + for (String routerVersion : versionList) { + int num = versions.count(routerVersion); + buf.append("\n"); } buf.append("
Core versionRouter versionNumber
").append(DataHelper.stripHTML(coreVersion)); - buf.append("").append(DataHelper.stripHTML(routerVersion)); - buf.append("").append(num.intValue()).append("
VersionCount
").append(DataHelper.stripHTML(routerVersion)); + buf.append("").append(num).append("
\n"); } + buf.append("
"); + out.write(buf.toString()); + buf.setLength(0); + + List countryList = new ArrayList(countries.objects()); + if (countryList.size() > 0) { + Collections.sort(countryList); + buf.append("\n"); + buf.append("\n"); + for (String country : countryList) { + int num = countries.count(country); + buf.append("\n"); + } + buf.append("
CountryCount
\"").append(country.toUpperCase()).append("\""); "); + buf.append(_context.commSystem().getCountryName(country)); + buf.append("").append(num).append("
\n"); + } + buf.append("
"); out.write(buf.toString()); out.flush(); } diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index a798e0c18..04d4f8d23 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -433,6 +433,16 @@ public class CommSystemFacadeImpl extends CommSystemFacade { return props.getProperty("host"); } + /** full name for a country code, or the code if we don't know the name */ + public String getCountryName(String c) { + if (_geoIP == null) + return c; + String n = _geoIP.fullName(c); + if (n == null) + return c; + return n; + } + /** Provide a consistent "look" for displaying router IDs in the console */ public String renderPeerHTML(Hash peer) { String h = peer.toBase64().substring(0, 4); @@ -440,11 +450,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade { String c = getCountry(peer); if (c != null) { buf.append("\"").append(c.toUpperCase()).append("\" "); } buf.append(""); diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index 9845a72f3..7293b6a1e 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -29,6 +29,7 @@ import net.i2p.router.tunnel.HopConfig; import net.i2p.stat.RateStat; import net.i2p.util.I2PThread; import net.i2p.util.Log; +import net.i2p.util.ObjectCounter; /** * @@ -588,15 +589,15 @@ public class TunnelPoolManager implements TunnelManagerFacade { private void renderPeers(Writer out) throws IOException { // count up the peers in the local pools - HashCounter lc = new HashCounter(); + ObjectCounter lc = new ObjectCounter(); int tunnelCount = countTunnelsPerPeer(lc); // count up the peers in the participating tunnels - HashCounter pc = new HashCounter(); + ObjectCounter pc = new ObjectCounter(); int partCount = countParticipatingPerPeer(pc); - Set peers = new HashSet(lc.hashes()); - peers.addAll(pc.hashes()); + Set peers = new HashSet(lc.objects()); + peers.addAll(pc.objects()); List peerList = new ArrayList(peers); Collections.sort(peerList, new HashComparator()); @@ -625,7 +626,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { } /** @return total number of non-fallback expl. + client tunnels */ - private int countTunnelsPerPeer(HashCounter lc) { + private int countTunnelsPerPeer(ObjectCounter lc) { List pools = new ArrayList(); listPools(pools); int tunnelCount = 0; @@ -661,12 +662,12 @@ public class TunnelPoolManager implements TunnelManagerFacade { * @return Set of peers that should not be allowed in another tunnel */ public Set selectPeersInTooManyTunnels() { - HashCounter lc = new HashCounter(); + ObjectCounter lc = new ObjectCounter(); int tunnelCount = countTunnelsPerPeer(lc); Set rv = new HashSet(); if (tunnelCount >= 4 && _context.router().getUptime() > 10*60*1000) { int max = _context.getProperty("router.maxTunnelPercentage", DEFAULT_MAX_PCT_TUNNELS); - for (Hash h : lc.hashes()) { + for (Hash h : lc.objects()) { if (lc.count(h) > 0 && (lc.count(h) + 1) * 100 / (tunnelCount + 1) > max) rv.add(h); } @@ -675,7 +676,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { } /** @return total number of part. tunnels */ - private int countParticipatingPerPeer(HashCounter pc) { + private int countParticipatingPerPeer(ObjectCounter pc) { List participating = _context.tunnelDispatcher().listParticipatingTunnels(); for (HopConfig cfg : participating) { Hash from = cfg.getReceiveFrom(); @@ -694,27 +695,6 @@ public class TunnelPoolManager implements TunnelManagerFacade { } } - private static class HashCounter { - private ConcurrentHashMap _map; - public HashCounter() { - _map = new ConcurrentHashMap(); - } - public void increment(Hash h) { - Integer i = _map.putIfAbsent(h, Integer.valueOf(1)); - if (i != null) - _map.put(h, Integer.valueOf(i.intValue() + 1)); - } - public int count(Hash h) { - Integer i = _map.get(h); - if (i != null) - return i.intValue(); - return 0; - } - public Set hashes() { - return _map.keySet(); - } - } - private String getCapacity(Hash peer) { RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); if (info != null) { From c89fb9ad734f10d2dce48e27b195cddde09ca7d6 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 17 Jun 2009 12:37:03 +0000 Subject: [PATCH 25/58] * PeerSelector: - Limit exploratory tunnels to connected peers when over half the connection limit (was 80%) - Have the high capacity tier fall back to a new connected tier before moving on to the not failing tier so that tunnel build success doesn't collapse * PeerTestJob: - Limit to connected peers --- history.txt | 11 +++++ .../src/net/i2p/router/CommSystemFacade.java | 1 + .../src/net/i2p/router/RouterVersion.java | 2 +- .../i2p/router/peermanager/PeerManager.java | 9 +++- .../router/peermanager/ProfileOrganizer.java | 42 +++++++++++++++++-- .../transport/CommSystemFacadeImpl.java | 2 + .../net/i2p/router/transport/Transport.java | 1 + .../router/transport/TransportManager.java | 14 +++++++ .../router/transport/ntcp/NTCPTransport.java | 4 ++ .../router/transport/udp/UDPTransport.java | 6 +++ .../tunnel/pool/ExploratoryPeerSelector.java | 2 +- 11 files changed, 87 insertions(+), 7 deletions(-) diff --git a/history.txt b/history.txt index 87301b3cb..28b3be69d 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,14 @@ +2009-06-17 zzz + * PeerSelector: + - Limit exploratory tunnels to connected peers when over + half the connection limit (was 80%) + - Have the high capacity tier fall back to a new connected tier + before moving on to the not failing tier + so that tunnel build success doesn't collapse and drive + connections to the limit + * PeerTestJob: + - Limit to connected peers + 2009-06-12 zzz * Console: - Move the console css from default.css in the .war to docs/themes/console/console.css, diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java index 2e85a7eb5..436501324 100644 --- a/router/java/src/net/i2p/router/CommSystemFacade.java +++ b/router/java/src/net/i2p/router/CommSystemFacade.java @@ -36,6 +36,7 @@ public abstract class CommSystemFacade implements Service { public int countActiveSendPeers() { return 0; } public boolean haveInboundCapacity() { return true; } public boolean haveOutboundCapacity() { return true; } + public boolean haveHighOutboundCapacity() { return true; } public List getMostRecentErrorMessages() { return Collections.EMPTY_LIST; } /** diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index b74a3a021..bd644cee5 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ 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 = 2; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; diff --git a/router/java/src/net/i2p/router/peermanager/PeerManager.java b/router/java/src/net/i2p/router/peermanager/PeerManager.java index 7086f1cfc..9dc3a859b 100644 --- a/router/java/src/net/i2p/router/peermanager/PeerManager.java +++ b/router/java/src/net/i2p/router/peermanager/PeerManager.java @@ -116,7 +116,14 @@ class PeerManager { case PeerSelectionCriteria.PURPOSE_TEST: // for now, the peers we test will be the reliable ones //_organizer.selectWellIntegratedPeers(criteria.getMinimumRequired(), exclude, curVals); - _organizer.selectNotFailingPeers(criteria.getMinimumRequired(), exclude, peers); + + // The PeerTestJob does only run every 5 minutes, but + // this was helping drive us to connection limits, let's leave the exploration + // to the ExploratoryPeerSelector, which will restrict to connected peers + // when we get close to the limit. So let's stick with connected peers here. + // Todo: what's the point of the PeerTestJob anyway? + //_organizer.selectNotFailingPeers(criteria.getMinimumRequired(), exclude, peers); + _organizer.selectActiveNotFailingPeers(criteria.getMinimumRequired(), exclude, peers); break; case PeerSelectionCriteria.PURPOSE_TUNNEL: // pull all of the fast ones, regardless of how many we diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index 9d9a8b746..b0de2092d 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -316,8 +316,8 @@ public class ProfileOrganizer { } finally { releaseReadLock(); } if (matches.size() < howMany) { if (_log.shouldLog(Log.INFO)) - _log.info("selectHighCap("+howMany+"), not enough fast (" + matches.size() + ") going on to notFailing"); - selectNotFailingPeers(howMany, exclude, matches, mask); + _log.info("selectHighCap("+howMany+"), not enough highcap (" + matches.size() + ") going on to ANFP2"); + selectActiveNotFailingPeers2(howMany, exclude, matches, mask); } else { if (_log.shouldLog(Log.INFO)) _log.info("selectHighCap("+howMany+"), found enough highCap (" + matches.size() + ")"); @@ -375,6 +375,7 @@ public class ProfileOrganizer { selectAllNotFailingPeers(howMany, exclude, matches, onlyNotFailing, mask); return; } + /** * Return a set of Hashes for peers that are both not failing and we're actively * talking with. @@ -403,6 +404,39 @@ public class ProfileOrganizer { } } + /** + * Return a set of Hashes for peers that are both not failing and we're actively + * talking with. + * + * We use commSystem().isEstablished(), not profile.getIsActive(), as the + * NTCP idle time is now shorter than the 5 minute getIsActive() threshold, + * and we're using this to try and limit connections. + * + * This DOES cascade further to non-connected peers. + */ + private void selectActiveNotFailingPeers2(int howMany, Set exclude, Set matches, int mask) { + if (matches.size() < howMany) { + Map activePeers = new HashMap(); + getReadLock(); + try { + for (Iterator> iter = _notFailingPeers.entrySet().iterator(); iter.hasNext(); ) { + Map.Entry e = iter.next(); + if (_context.commSystem().isEstablished(e.getKey())) + activePeers.put(e.getKey(), e.getValue()); + } + locked_selectPeers(activePeers, howMany, exclude, matches, mask); + } finally { releaseReadLock(); } + } + if (matches.size() < howMany) { + if (_log.shouldLog(Log.INFO)) + _log.info("selectANFP2("+howMany+"), not enough ANFP (" + matches.size() + ") going on to notFailing"); + selectNotFailingPeers(howMany, exclude, matches, mask); + } else { + if (_log.shouldLog(Log.INFO)) + _log.info("selectANFP2("+howMany+"), found enough ANFP (" + matches.size() + ")"); + } + } + /** * Return a set of Hashes for peers that are not failing. * @@ -520,8 +554,8 @@ public class ProfileOrganizer { } } } - if (_log.shouldLog(Log.INFO)) - _log.info("Unreachable: " + l); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Unreachable: " + l); return l; } diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index a798e0c18..528337239 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -73,6 +73,8 @@ public class CommSystemFacadeImpl extends CommSystemFacade { public boolean haveInboundCapacity() { return (_manager == null ? false : _manager.haveInboundCapacity()); } @Override public boolean haveOutboundCapacity() { return (_manager == null ? false : _manager.haveOutboundCapacity()); } + @Override + public boolean haveHighOutboundCapacity() { return (_manager == null ? false : _manager.haveHighOutboundCapacity()); } /** * Framed average clock skew of connected peers in seconds, or null if we cannot answer. diff --git a/router/java/src/net/i2p/router/transport/Transport.java b/router/java/src/net/i2p/router/transport/Transport.java index fc5c32153..d0ec7267b 100644 --- a/router/java/src/net/i2p/router/transport/Transport.java +++ b/router/java/src/net/i2p/router/transport/Transport.java @@ -47,6 +47,7 @@ public interface Transport { public int countActivePeers(); public int countActiveSendPeers(); public boolean haveCapacity(); + public boolean haveHighCapacity(); public Vector getClockSkews(); public List getMostRecentErrorMessages(); diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index 9afdaf333..c61ed14f8 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -215,6 +215,20 @@ public class TransportManager implements TransportEventListener { return false; } + /** + * Are all transports well below their outbound connection limit + * Use for throttling in the router. + */ + public boolean haveHighOutboundCapacity() { + if (_transports.size() <= 0) + return false; + for (int i = 0; i < _transports.size(); i++) { + if (!((Transport)_transports.get(i)).haveHighCapacity()) + return false; + } + return true; + } + /** * Is at least one transport below its inbound connection limit + some margin * Use for throttling in the router. diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index 75564fb9e..9f6b8e7ca 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -326,6 +326,10 @@ public class NTCPTransport extends TransportImpl { return countActivePeers() < getMaxConnections() * 4 / 5; } + public boolean haveHighCapacity() { + return countActivePeers() < getMaxConnections() / 2; + } + /** queue up afterSend call, which can take some time w/ jobs, etc */ void sendComplete(OutNetMessage msg) { _finisher.add(msg); } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 4790fb122..b42efbfae 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -1382,6 +1382,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority } } + public boolean haveHighCapacity() { + synchronized (_peersByIdent) { + return _peersByIdent.size() < getMaxConnections() / 2; + } + } + /** * Return our peer clock skews on this transport. * Vector composed of Long, each element representing a peer skew in seconds. diff --git a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java index b324990de..8971b8ba3 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java @@ -47,7 +47,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { // if (exploreHighCap) ctx.profileOrganizer().selectHighCapacityPeers(length, exclude, matches); - else if (ctx.commSystem().haveOutboundCapacity()) + else if (ctx.commSystem().haveHighOutboundCapacity()) ctx.profileOrganizer().selectNotFailingPeers(length, exclude, matches, false); else // use only connected peers so we don't make more connections ctx.profileOrganizer().selectActiveNotFailingPeers(length, exclude, matches); From 22c2829714a89c46db2928f7ef5beb703ca97acf Mon Sep 17 00:00:00 2001 From: mathiasdm Date: Wed, 17 Jun 2009 21:54:29 +0000 Subject: [PATCH 26/58] * desktopgui: - Added client and server tunnel view (saving does not work yet) --- apps/desktopgui/build.xml | 2 + apps/desktopgui/nbproject/project.properties | 4 +- .../i2p/desktopgui/desktopgui/GUIVersion.java | 2 +- .../desktopgui/gui/ClientTunnelWindow.form | 396 ++++++++++++++++ .../desktopgui/gui/ClientTunnelWindow.java | 434 ++++++++++++++++++ .../desktopgui/gui/GeneralConfiguration.form | 106 ++++- .../desktopgui/gui/GeneralConfiguration.java | 183 +++++++- .../desktopgui/gui/ServerTunnelWindow.form | 40 ++ .../desktopgui/gui/ServerTunnelWindow.java | 60 +++ .../resources/ClientTunnelWindow.properties | 26 ++ .../resources/GeneralConfiguration.properties | 12 +- .../resources/ServerTunnelWindow.properties | 1 + history.txt | 5 + 13 files changed, 1242 insertions(+), 29 deletions(-) create mode 100644 apps/desktopgui/src/net/i2p/desktopgui/gui/ClientTunnelWindow.form create mode 100644 apps/desktopgui/src/net/i2p/desktopgui/gui/ClientTunnelWindow.java create mode 100644 apps/desktopgui/src/net/i2p/desktopgui/gui/ServerTunnelWindow.form create mode 100644 apps/desktopgui/src/net/i2p/desktopgui/gui/ServerTunnelWindow.java create mode 100644 apps/desktopgui/src/net/i2p/desktopgui/gui/resources/ClientTunnelWindow.properties create mode 100644 apps/desktopgui/src/net/i2p/desktopgui/gui/resources/ServerTunnelWindow.properties diff --git a/apps/desktopgui/build.xml b/apps/desktopgui/build.xml index 795edf046..8138f14c5 100644 --- a/apps/desktopgui/build.xml +++ b/apps/desktopgui/build.xml @@ -77,11 +77,13 @@ + + diff --git a/apps/desktopgui/nbproject/project.properties b/apps/desktopgui/nbproject/project.properties index fb880932f..a02ea8207 100644 --- a/apps/desktopgui/nbproject/project.properties +++ b/apps/desktopgui/nbproject/project.properties @@ -22,6 +22,7 @@ dist.javadoc.dir=${dist.dir}/javadoc excludes= file.reference.appframework.jar=lib/appframework.jar file.reference.i2p.jar=../../core/java/build/i2p.jar +file.reference.i2ptunnel.jar=../i2ptunnel/java/build/i2ptunnel.jar file.reference.router.jar=../../router/java/build/router.jar file.reference.routerconsole.jar=../routerconsole/java/build/routerconsole.jar file.reference.swing-worker.jar=lib/swing-worker.jar @@ -32,7 +33,8 @@ javac.classpath=\ ${file.reference.appframework.jar}:\ ${file.reference.swing-worker.jar}:\ ${file.reference.i2p.jar}:\ - ${file.reference.routerconsole.jar} + ${file.reference.routerconsole.jar}:\ + ${file.reference.i2ptunnel.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false diff --git a/apps/desktopgui/src/net/i2p/desktopgui/desktopgui/GUIVersion.java b/apps/desktopgui/src/net/i2p/desktopgui/desktopgui/GUIVersion.java index 10a6e5293..21f0e04cf 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/desktopgui/GUIVersion.java +++ b/apps/desktopgui/src/net/i2p/desktopgui/desktopgui/GUIVersion.java @@ -10,5 +10,5 @@ package net.i2p.desktopgui.desktopgui; * @author mathias */ public class GUIVersion { - public static final String VERSION = "0.0.1.3"; + public static final String VERSION = "0.0.2"; } diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/ClientTunnelWindow.form b/apps/desktopgui/src/net/i2p/desktopgui/gui/ClientTunnelWindow.form new file mode 100644 index 000000000..0f07284d5 --- /dev/null +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/ClientTunnelWindow.form @@ -0,0 +1,396 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/ClientTunnelWindow.java b/apps/desktopgui/src/net/i2p/desktopgui/gui/ClientTunnelWindow.java new file mode 100644 index 000000000..b7102b3ce --- /dev/null +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/ClientTunnelWindow.java @@ -0,0 +1,434 @@ +/* + * ClientTunnelWindow.java + * + * Created on 10-jun-2009, 16:49:12 + */ + +package net.i2p.desktopgui.gui; + +import net.i2p.i2ptunnel.web.EditBean; +import java.awt.event.ActionListener; + +/** + * + * @author mathias + */ +public class ClientTunnelWindow extends javax.swing.JFrame { + + /** Creates new form ClientTunnelWindow */ + public ClientTunnelWindow(int tunnelNumber, ActionListener al) { + initComponents(); + this.tunnelNumber = tunnelNumber; + this.al = al; + extraInitComponents(); + this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + this.setSize(600, 600); + this.setLocationRelativeTo(null); + this.requestFocus(); + this.changeTunnelState.setVisible(false); //TODO: implement tunnel state change + this.setVisible(true); + } + + public void extraInitComponents() { + EditBean bean = new EditBean(); + if(!bean.isClient(tunnelNumber)) { + this.dispose(); + } + else { + this.tunnelName.setText(bean.getTunnelName(tunnelNumber)); + this.tunnelType.setText(bean.getTunnelType(tunnelNumber)); + this.tunnelPort.setText(bean.getClientPort(tunnelNumber)); + this.tunnelDestination.setText(bean.getClientDestination(tunnelNumber)); + + if(bean.getTunnelType(tunnelNumber).equals(TYPE_STREAMR_CLIENT)) { + tunnelProfile.setVisible(false); + tunnelProfileLabel.setVisible(false); + this.delayConnect.setVisible(false); + this.sharedClient.setVisible(false); + this.autoStart.setVisible(false); + } + else { + if(bean.isInteractive(tunnelNumber)) { + tunnelProfile.setSelectedIndex(TUNNEL_INTERACTIVE); + } + else { + tunnelProfile.setSelectedIndex(TUNNEL_BULK); + } + + this.delayConnect.setSelected(bean.shouldDelay(tunnelNumber)); + this.sharedClient.setSelected(bean.isSharedClient(tunnelNumber)); + this.autoStart.setSelected(bean.startAutomatically(tunnelNumber)); + } + + this.tunnelDepth.setSelectedIndex(bean.getTunnelDepth(tunnelNumber, 2)); + + int variance = bean.getTunnelVariance(tunnelNumber, 0); + if(variance == 0) { + this.depthVariance.setSelectedIndex(0); + } + else if(variance == 1) { + this.depthVariance.setSelectedIndex(1); + } + else if(variance == 2) { + this.depthVariance.setSelectedIndex(2); + } + else if(variance == -1) { + this.depthVariance.setSelectedIndex(3); + } + else if(variance == -2) { + this.depthVariance.setSelectedIndex(4); + } + + int tunnelQuantity = bean.getTunnelQuantity(tunnelNumber, 2) - 1; + if(tunnelQuantity >= 0 && tunnelQuantity <= 2) { + this.tunnelCount.setSelectedIndex(tunnelQuantity); + } + + int backupTunnelQuantity = bean.getTunnelBackupQuantity(tunnelNumber, 0); + if(backupTunnelQuantity >= 0 && backupTunnelQuantity <= 3) { + this.backupTunnelCount.setSelectedIndex(backupTunnelQuantity); + } + + + if(bean.getTunnelType(tunnelNumber).equals(TYPE_STREAMR_CLIENT)) { + this.reduceIdle.setVisible(false); + this.closeIdle.setVisible(false); + this.delayIdle.setVisible(false); + } + else { + this.reduceIdle.setSelected(bean.getReduce(tunnelNumber)); + this.closeIdle.setSelected(bean.getClose(tunnelNumber)); + this.delayIdle.setSelected(bean.getDelayOpen(tunnelNumber)); + } + } + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + tunnelNameLabel = new javax.swing.JLabel(); + tunnelTypeLabel = new javax.swing.JLabel(); + tunnelPortLabel = new javax.swing.JLabel(); + tunnelDestinationLabel = new javax.swing.JLabel(); + tunnelProfileLabel = new javax.swing.JLabel(); + delayConnect = new javax.swing.JCheckBox(); + sharedClient = new javax.swing.JCheckBox(); + autoStart = new javax.swing.JCheckBox(); + jSeparator1 = new javax.swing.JSeparator(); + tunnelDepthLabel = new javax.swing.JLabel(); + depthVarianceLabel = new javax.swing.JLabel(); + tunnelCountLabel = new javax.swing.JLabel(); + backupTunnelCountLabel = new javax.swing.JLabel(); + jSeparator2 = new javax.swing.JSeparator(); + reduceIdle = new javax.swing.JCheckBox(); + closeIdle = new javax.swing.JCheckBox(); + delayIdle = new javax.swing.JCheckBox(); + jSeparator3 = new javax.swing.JSeparator(); + save = new javax.swing.JButton(); + cancel = new javax.swing.JButton(); + tunnelName = new javax.swing.JTextField(); + tunnelType = new javax.swing.JLabel(); + tunnelPort = new javax.swing.JTextField(); + tunnelDestination = new javax.swing.JTextField(); + tunnelProfile = new javax.swing.JComboBox(); + tunnelDepth = new javax.swing.JComboBox(); + depthVariance = new javax.swing.JComboBox(); + tunnelCount = new javax.swing.JComboBox(); + backupTunnelCount = new javax.swing.JComboBox(); + changeTunnelState = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(net.i2p.desktopgui.desktopgui.Main.class).getContext().getResourceMap(ClientTunnelWindow.class); + setTitle(resourceMap.getString("Form.title")); // NOI18N + setName("Form"); // NOI18N + getContentPane().setLayout(null); + + tunnelNameLabel.setText(resourceMap.getString("tunnelNameLabel.text")); // NOI18N + tunnelNameLabel.setName("tunnelNameLabel"); // NOI18N + getContentPane().add(tunnelNameLabel); + tunnelNameLabel.setBounds(20, 20, 120, 17); + + tunnelTypeLabel.setText(resourceMap.getString("tunnelTypeLabel.text")); // NOI18N + tunnelTypeLabel.setName("tunnelTypeLabel"); // NOI18N + getContentPane().add(tunnelTypeLabel); + tunnelTypeLabel.setBounds(20, 50, 120, 17); + + tunnelPortLabel.setText(resourceMap.getString("tunnelPortLabel.text")); // NOI18N + tunnelPortLabel.setName("tunnelPortLabel"); // NOI18N + getContentPane().add(tunnelPortLabel); + tunnelPortLabel.setBounds(20, 80, 110, 17); + + tunnelDestinationLabel.setText(resourceMap.getString("tunnelDestinationLabel.text")); // NOI18N + tunnelDestinationLabel.setName("tunnelDestinationLabel"); // NOI18N + getContentPane().add(tunnelDestinationLabel); + tunnelDestinationLabel.setBounds(20, 110, 110, 17); + + tunnelProfileLabel.setText(resourceMap.getString("tunnelProfileLabel.text")); // NOI18N + tunnelProfileLabel.setName("tunnelProfileLabel"); // NOI18N + getContentPane().add(tunnelProfileLabel); + tunnelProfileLabel.setBounds(20, 140, 110, 17); + + delayConnect.setText(resourceMap.getString("delayConnect.text")); // NOI18N + delayConnect.setName("delayConnect"); // NOI18N + getContentPane().add(delayConnect); + delayConnect.setBounds(20, 170, 160, 22); + + sharedClient.setText(resourceMap.getString("sharedClient.text")); // NOI18N + sharedClient.setName("sharedClient"); // NOI18N + getContentPane().add(sharedClient); + sharedClient.setBounds(20, 200, 160, 22); + + autoStart.setText(resourceMap.getString("autoStart.text")); // NOI18N + autoStart.setName("autoStart"); // NOI18N + getContentPane().add(autoStart); + autoStart.setBounds(20, 230, 160, 22); + + jSeparator1.setName("jSeparator1"); // NOI18N + getContentPane().add(jSeparator1); + jSeparator1.setBounds(0, 510, 750, 10); + + tunnelDepthLabel.setText(resourceMap.getString("tunnelDepthLabel.text")); // NOI18N + tunnelDepthLabel.setName("tunnelDepthLabel"); // NOI18N + getContentPane().add(tunnelDepthLabel); + tunnelDepthLabel.setBounds(20, 280, 160, 17); + + depthVarianceLabel.setText(resourceMap.getString("depthVarianceLabel.text")); // NOI18N + depthVarianceLabel.setName("depthVarianceLabel"); // NOI18N + getContentPane().add(depthVarianceLabel); + depthVarianceLabel.setBounds(20, 310, 160, 17); + + tunnelCountLabel.setText(resourceMap.getString("tunnelCountLabel.text")); // NOI18N + tunnelCountLabel.setName("tunnelCountLabel"); // NOI18N + getContentPane().add(tunnelCountLabel); + tunnelCountLabel.setBounds(20, 340, 160, 17); + + backupTunnelCountLabel.setText(resourceMap.getString("backupTunnelCountLabel.text")); // NOI18N + backupTunnelCountLabel.setName("backupTunnelCountLabel"); // NOI18N + getContentPane().add(backupTunnelCountLabel); + backupTunnelCountLabel.setBounds(20, 370, 170, 17); + + jSeparator2.setName("jSeparator2"); // NOI18N + getContentPane().add(jSeparator2); + jSeparator2.setBounds(0, 260, 750, 10); + + reduceIdle.setText(resourceMap.getString("reduceIdle.text")); // NOI18N + reduceIdle.setName("reduceIdle"); // NOI18N + getContentPane().add(reduceIdle); + reduceIdle.setBounds(20, 420, 300, 22); + + closeIdle.setText(resourceMap.getString("closeIdle.text")); // NOI18N + closeIdle.setName("closeIdle"); // NOI18N + getContentPane().add(closeIdle); + closeIdle.setBounds(20, 450, 370, 22); + + delayIdle.setText(resourceMap.getString("delayIdle.text")); // NOI18N + delayIdle.setName("delayIdle"); // NOI18N + getContentPane().add(delayIdle); + delayIdle.setBounds(20, 480, 400, 22); + + jSeparator3.setName("jSeparator3"); // NOI18N + getContentPane().add(jSeparator3); + jSeparator3.setBounds(0, 400, 760, 10); + + save.setText(resourceMap.getString("save.text")); // NOI18N + save.setName("save"); // NOI18N + save.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + saveActionPerformed(evt); + } + }); + getContentPane().add(save); + save.setBounds(10, 520, 44, 29); + + cancel.setText(resourceMap.getString("cancel.text")); // NOI18N + cancel.setName("cancel"); // NOI18N + cancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelActionPerformed(evt); + } + }); + getContentPane().add(cancel); + cancel.setBounds(60, 520, 55, 29); + + tunnelName.setText(resourceMap.getString("tunnelName.text")); // NOI18N + tunnelName.setName("tunnelName"); // NOI18N + getContentPane().add(tunnelName); + tunnelName.setBounds(200, 20, 340, 27); + + tunnelType.setText(resourceMap.getString("tunnelType.text")); // NOI18N + tunnelType.setName("tunnelType"); // NOI18N + getContentPane().add(tunnelType); + tunnelType.setBounds(200, 50, 340, 20); + + tunnelPort.setText(resourceMap.getString("tunnelPort.text")); // NOI18N + tunnelPort.setName("tunnelPort"); // NOI18N + getContentPane().add(tunnelPort); + tunnelPort.setBounds(200, 70, 340, 27); + + tunnelDestination.setText(resourceMap.getString("tunnelDestination.text")); // NOI18N + tunnelDestination.setName("tunnelDestination"); // NOI18N + getContentPane().add(tunnelDestination); + tunnelDestination.setBounds(200, 100, 340, 27); + + tunnelProfile.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Interactive connection (Instant messaging)", "Bulk connection (Downloads, websites...)" })); + tunnelProfile.setName("tunnelProfile"); // NOI18N + getContentPane().add(tunnelProfile); + tunnelProfile.setBounds(200, 130, 340, 27); + + tunnelDepth.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "0 hop tunnel (no anonymity, low latency)", "1 hop tunnel (low anonymity, low latency)", "2 hop tunnel (medium anonymity, medium latency)", "3 hop tunnel (high anonymity, high latency)" })); + tunnelDepth.setName("tunnelDepth"); // NOI18N + getContentPane().add(tunnelDepth); + tunnelDepth.setBounds(200, 280, 350, 27); + + depthVariance.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "0 hop variance (no random, good performance)", "+ 0-1 hop variance (slightly random, lower performance)", "+ 0-2 hop variance (very random, lower performance)", "+/- 0-1 hop variance (slightly random, standard performance)", "+/- 0-2 hop variance (not recommended)" })); + depthVariance.setName("depthVariance"); // NOI18N + getContentPane().add(depthVariance); + depthVariance.setBounds(200, 310, 350, 27); + + tunnelCount.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "1 tunnel (low bandwidth usage, low reliability)", "2 tunnels (standard bandwidth usage, standard reliability)", "3 tunnels (high bandwidth usage, high reliability)" })); + tunnelCount.setName("tunnelCount"); // NOI18N + getContentPane().add(tunnelCount); + tunnelCount.setBounds(200, 340, 350, 27); + + backupTunnelCount.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "0 backup tunnels (no redundancy, no resource usage)", "1 backup tunnel (low redundancy, low resource usage)", "2 backup tunnels (medium redundancy, medium resource usage)", "3 backup tunnels (high redundancy, high resource usage)" })); + backupTunnelCount.setName("backupTunnelCount"); // NOI18N + getContentPane().add(backupTunnelCount); + backupTunnelCount.setBounds(200, 370, 350, 27); + + changeTunnelState.setText(resourceMap.getString("changeTunnelState.text")); // NOI18N + changeTunnelState.setName("changeTunnelState"); // NOI18N + getContentPane().add(changeTunnelState); + changeTunnelState.setBounds(160, 520, 150, 29); + + pack(); + }// //GEN-END:initComponents + + private void saveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveActionPerformed + EditBean bean = new EditBean(); + if(!bean.isClient(tunnelNumber)) { + al.actionPerformed(evt); + this.dispose(); + } + else { + bean.setTunnel("" + tunnelNumber); + bean.setName(tunnelName.getText()); + bean.setPort(tunnelPort.getText()); + bean.setTargetDestination(tunnelDestination.getText()); + if(!bean.getTunnelType(tunnelNumber).equals(TYPE_STREAMR_CLIENT)) { + if(tunnelProfile.getSelectedIndex() == TUNNEL_INTERACTIVE) { + bean.setProfile("interactive"); + } + else { + bean.setProfile("bulk"); + } + + if(delayConnect.isSelected()) { + bean.setConnectDelay("true"); + } + else { + bean.setConnectDelay("false"); + } + + if(sharedClient.isSelected()) { + bean.setShared(true); + } + else { + bean.setShared(false); + } + + if(autoStart.isSelected()) { + bean.setStartOnLoad("true"); + } + else { + } + } + bean.setTunnelDepth("" + tunnelDepth.getSelectedIndex()); + + int variance = depthVariance.getSelectedIndex(); + if(variance >= 0 && variance <= 2) { + bean.setTunnelVariance("" + variance); + } + else if(variance == 3) { + bean.setTunnelVariance("-1"); + } + else if(variance == 4) { + bean.setTunnelVariance("-2"); + } + + bean.setTunnelQuantity("" + tunnelCount.getSelectedIndex() + 1); + + bean.setTunnelBackupQuantity("" + backupTunnelCount.getSelectedIndex()); + + if(!bean.getTunnelType(tunnelNumber).equals(TYPE_STREAMR_CLIENT)) { + if(reduceIdle.isSelected()) { + bean.setReduce("true"); + } + else { + } + + if(closeIdle.isSelected()) { + bean.setClose("true"); + } + else { + } + + if(delayIdle.isSelected()) { + bean.setDelayOpen("true"); + } + } + + } + + al.actionPerformed(evt); + this.dispose(); +}//GEN-LAST:event_saveActionPerformed + + private void cancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelActionPerformed + al.actionPerformed(evt); + this.dispose(); + }//GEN-LAST:event_cancelActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox autoStart; + private javax.swing.JComboBox backupTunnelCount; + private javax.swing.JLabel backupTunnelCountLabel; + private javax.swing.JButton cancel; + private javax.swing.JButton changeTunnelState; + private javax.swing.JCheckBox closeIdle; + private javax.swing.JCheckBox delayConnect; + private javax.swing.JCheckBox delayIdle; + private javax.swing.JComboBox depthVariance; + private javax.swing.JLabel depthVarianceLabel; + private javax.swing.JSeparator jSeparator1; + private javax.swing.JSeparator jSeparator2; + private javax.swing.JSeparator jSeparator3; + private javax.swing.JCheckBox reduceIdle; + private javax.swing.JButton save; + private javax.swing.JCheckBox sharedClient; + private javax.swing.JComboBox tunnelCount; + private javax.swing.JLabel tunnelCountLabel; + private javax.swing.JComboBox tunnelDepth; + private javax.swing.JLabel tunnelDepthLabel; + private javax.swing.JTextField tunnelDestination; + private javax.swing.JLabel tunnelDestinationLabel; + private javax.swing.JTextField tunnelName; + private javax.swing.JLabel tunnelNameLabel; + private javax.swing.JTextField tunnelPort; + private javax.swing.JLabel tunnelPortLabel; + private javax.swing.JComboBox tunnelProfile; + private javax.swing.JLabel tunnelProfileLabel; + private javax.swing.JLabel tunnelType; + private javax.swing.JLabel tunnelTypeLabel; + // End of variables declaration//GEN-END:variables + private int tunnelNumber; + private ActionListener al; + private static final int TUNNEL_INTERACTIVE = 0; + private static final int TUNNEL_BULK = 1; + private static final String TYPE_STREAMR_CLIENT = "Streamr client"; +} diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.form b/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.form index 4c7cb5afc..589f5cee2 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.form +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.form @@ -322,14 +322,14 @@ - - - - + + + + - + @@ -439,11 +439,11 @@ - + - - + + @@ -463,9 +463,9 @@ - - - + + + @@ -477,6 +477,51 @@ + + + + + + + + + +
+
+ + + + + <Editor/> + <Renderer/> + </Column> + <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> + <Title resourceKey="clientTable.columnModel.title1"/> + <Editor/> + <Renderer/> + </Column> + <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> + <Title resourceKey="clientTable.columnModel.title2"/> + <Editor/> + <Renderer/> + </Column> + <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> + <Title resourceKey="clientTable.columnModel.title3"/> + <Editor/> + <Renderer/> + </Column> + </TableColumnModel> + </Property> + <Property name="name" type="java.lang.String" value="clientTable" noResource="true"/> + <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> + <TableHeader reorderingAllowed="true" resizingAllowed="true"/> + </Property> + </Properties> + <Events> + <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="clientTableMouseClicked"/> + </Events> + </Component> + </SubComponents> </Container> <Container class="javax.swing.JScrollPane" name="serverFrame"> <Properties> @@ -484,6 +529,45 @@ </Properties> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> + <SubComponents> + <Component class="javax.swing.JTable" name="serverTable"> + <Properties> + <Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor"> + <Table columnCount="3" rowCount="4"> + <Column editable="true" title="Name" type="java.lang.Object"/> + <Column editable="true" title="Address" type="java.lang.Object"/> + <Column editable="true" title="Status" type="java.lang.Object"/> + </Table> + </Property> + <Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor"> + <TableColumnModel selectionModel="0"> + <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> + <Title resourceKey="serverTable.columnModel.title0"/> + <Editor/> + <Renderer/> + </Column> + <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> + <Title resourceKey="serverTable.columnModel.title1"/> + <Editor/> + <Renderer/> + </Column> + <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> + <Title resourceKey="serverTable.columnModel.title2"/> + <Editor/> + <Renderer/> + </Column> + </TableColumnModel> + </Property> + <Property name="name" type="java.lang.String" value="serverTable" noResource="true"/> + <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> + <TableHeader reorderingAllowed="true" resizingAllowed="true"/> + </Property> + </Properties> + <Events> + <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="serverTableMouseClicked"/> + </Events> + </Component> + </SubComponents> </Container> <Component class="javax.swing.JLabel" name="tunnelsExplanation"> <Properties> diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.java b/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.java index 474e39473..3e4a17fd2 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.java +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.java @@ -7,6 +7,7 @@ package net.i2p.desktopgui.gui; import java.awt.Desktop; +import java.awt.event.ActionEvent; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -23,6 +24,9 @@ import net.i2p.router.web.NewsFetcher; import net.i2p.desktopgui.router.configuration.UpdateHandler; import java.util.Date; import javax.swing.SwingWorker; +import net.i2p.i2ptunnel.web.IndexBean; +import javax.swing.table.DefaultTableModel; +import java.awt.event.ActionListener; /** * @@ -43,6 +47,7 @@ public class GeneralConfiguration extends javax.swing.JFrame { private void extraInitComponents() { initSpeedTab(); initUpdateTab(); + initTunnelTab(); } private void initSpeedTab() { @@ -87,6 +92,48 @@ public class GeneralConfiguration extends javax.swing.JFrame { } } + private void initTunnelTab() { + while(((DefaultTableModel) clientTable.getModel()).getRowCount() > 0) { + ((DefaultTableModel) clientTable.getModel()).removeRow(0); + } + while(((DefaultTableModel) serverTable.getModel()).getRowCount() > 0) { + ((DefaultTableModel) serverTable.getModel()).removeRow(0); + } + IndexBean bean = new IndexBean(); + for(int i=0; i<bean.getTunnelCount(); i++) { + if(bean.isClient(i)) { + Object[] row = {bean.getTunnelName(i), bean.getTunnelType(i), + bean.getClientInterface(i) + ":" + bean.getClientPort(i), + getTunnelStatus(bean.getTunnelStatus(i))}; + ((DefaultTableModel) clientTable.getModel()).addRow(row); + } + else { + Object[] row = {bean.getTunnelName(i), + bean.getServerTarget(i), + getTunnelStatus(bean.getTunnelStatus(i))}; + ((DefaultTableModel) serverTable.getModel()).addRow(row); + } + } + } + + public String getTunnelStatus(int status) { + if(status == IndexBean.NOT_RUNNING) { + return "Not running"; + } + else if(status == IndexBean.RUNNING) { + return "Running"; + } + else if(status == IndexBean.STANDBY) { + return "Standby"; + } + else if(status == IndexBean.STARTING) { + return "Starting"; + } + else { + return "Error: status not found"; + } + } + /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is @@ -125,7 +172,9 @@ public class GeneralConfiguration extends javax.swing.JFrame { advancedUpdateConfig = new javax.swing.JToggleButton(); tunnelPanel = new javax.swing.JPanel(); clientFrame = new javax.swing.JScrollPane(); + clientTable = new javax.swing.JTable(); serverFrame = new javax.swing.JScrollPane(); + serverTable = new javax.swing.JTable(); tunnelsExplanation = new javax.swing.JLabel(); clientTunnelLabel = new javax.swing.JLabel(); serverTunnelLabel = new javax.swing.JLabel(); @@ -334,11 +383,11 @@ public class GeneralConfiguration extends javax.swing.JFrame { .addComponent(updateNow)))) .addGroup(updatesPanelLayout.createSequentialGroup() .addGap(40, 40, 40) - .addGroup(updatesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(updateDownload) - .addComponent(updateInform) - .addComponent(updateDownloadRestart)))) - .addGap(9, 9, 9)) + .addGroup(updatesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(updateInform, javax.swing.GroupLayout.DEFAULT_SIZE, 377, Short.MAX_VALUE) + .addComponent(updateDownload, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(updateDownloadRestart, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addContainerGap()) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, updatesPanelLayout.createSequentialGroup() .addContainerGap(339, Short.MAX_VALUE) .addComponent(advancedUpdateConfig) @@ -370,8 +419,50 @@ public class GeneralConfiguration extends javax.swing.JFrame { clientFrame.setName("clientFrame"); // NOI18N + clientTable.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + + }, + new String [] { + "Name", "Type", "Address", "Status" + } + )); + clientTable.setName("clientTable"); // NOI18N + clientTable.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + clientTableMouseClicked(evt); + } + }); + clientFrame.setViewportView(clientTable); + clientTable.getColumnModel().getColumn(0).setHeaderValue(resourceMap.getString("clientTable.columnModel.title0")); // NOI18N + clientTable.getColumnModel().getColumn(1).setHeaderValue(resourceMap.getString("clientTable.columnModel.title1")); // NOI18N + clientTable.getColumnModel().getColumn(2).setHeaderValue(resourceMap.getString("clientTable.columnModel.title2")); // NOI18N + clientTable.getColumnModel().getColumn(3).setHeaderValue(resourceMap.getString("clientTable.columnModel.title3")); // NOI18N + serverFrame.setName("serverFrame"); // NOI18N + serverTable.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + {null, null, null}, + {null, null, null}, + {null, null, null}, + {null, null, null} + }, + new String [] { + "Name", "Address", "Status" + } + )); + serverTable.setName("serverTable"); // NOI18N + serverTable.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + serverTableMouseClicked(evt); + } + }); + serverFrame.setViewportView(serverTable); + serverTable.getColumnModel().getColumn(0).setHeaderValue(resourceMap.getString("serverTable.columnModel.title0")); // NOI18N + serverTable.getColumnModel().getColumn(1).setHeaderValue(resourceMap.getString("serverTable.columnModel.title1")); // NOI18N + serverTable.getColumnModel().getColumn(2).setHeaderValue(resourceMap.getString("serverTable.columnModel.title2")); // NOI18N + tunnelsExplanation.setText(resourceMap.getString("tunnelsExplanation.text")); // NOI18N tunnelsExplanation.setName("tunnelsExplanation"); // NOI18N @@ -385,14 +476,14 @@ public class GeneralConfiguration extends javax.swing.JFrame { tunnelPanel.setLayout(tunnelPanelLayout); tunnelPanelLayout.setHorizontalGroup( tunnelPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(tunnelPanelLayout.createSequentialGroup() + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tunnelPanelLayout.createSequentialGroup() .addContainerGap() - .addGroup(tunnelPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(serverFrame, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 538, Short.MAX_VALUE) - .addComponent(tunnelsExplanation, javax.swing.GroupLayout.DEFAULT_SIZE, 538, Short.MAX_VALUE) - .addComponent(clientTunnelLabel) - .addComponent(clientFrame, javax.swing.GroupLayout.DEFAULT_SIZE, 538, Short.MAX_VALUE) - .addComponent(serverTunnelLabel)) + .addGroup(tunnelPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(tunnelsExplanation, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 538, Short.MAX_VALUE) + .addComponent(serverFrame, javax.swing.GroupLayout.DEFAULT_SIZE, 538, Short.MAX_VALUE) + .addComponent(clientTunnelLabel, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(clientFrame, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 538, Short.MAX_VALUE) + .addComponent(serverTunnelLabel, javax.swing.GroupLayout.Alignment.LEADING)) .addContainerGap()) ); tunnelPanelLayout.setVerticalGroup( @@ -407,8 +498,8 @@ public class GeneralConfiguration extends javax.swing.JFrame { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(serverFrame, javax.swing.GroupLayout.DEFAULT_SIZE, 122, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(tunnelsExplanation, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addComponent(tunnelsExplanation, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(32, 32, 32)) ); settingsPanel.addTab(resourceMap.getString("tunnelPanel.TabConstraints.tabTitle"), tunnelPanel); // NOI18N @@ -584,6 +675,68 @@ private void advancedUpdateConfigActionPerformed(java.awt.event.ActionEvent evt) } }//GEN-LAST:event_advancedUpdateConfigActionPerformed +private void clientTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_clientTableMouseClicked + int row = clientTable.getSelectedRow(); + if(row == -1) { //No selected row + return; + } + else { + IndexBean bean = new IndexBean(); + /* + * TODO: This is not entirely good: if one adds/removes a tunnel without desktopgui, this number will be wrong + */ + int clientNumber = 0; + int i = 0; + for(clientNumber=0; clientNumber<bean.getTunnelCount(); clientNumber++) { + if(bean.isClient(clientNumber)) { + if(i == row) { + break; + } + i++; + } + } + new ClientTunnelWindow(clientNumber, new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + initTunnelTab(); + } + + }); + } +}//GEN-LAST:event_clientTableMouseClicked + +private void serverTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_serverTableMouseClicked + int row = serverTable.getSelectedRow(); + if(row == -1) { //No selected row + return; + } + else { + IndexBean bean = new IndexBean(); + /* + * TODO: This is not entirely good: if one adds/removes a tunnel without desktopgui, this number will be wrong + */ + int serverNumber = 0; + int i = 0; + for(serverNumber=0; serverNumber<bean.getTunnelCount(); serverNumber++) { + if(!bean.isClient(serverNumber)) { + if(i == row) { + break; + } + i++; + } + } + new ServerTunnelWindow(serverNumber, new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + initTunnelTab(); + } + + }); + } +}//GEN-LAST:event_serverTableMouseClicked + protected void initUsage(String upload, String download) { uploadgb.setText("" + SpeedHelper.calculateMonthlyUsage(Integer.parseInt(upload))); downloadgb.setText("" + SpeedHelper.calculateMonthlyUsage(Integer.parseInt(download))); @@ -646,6 +799,7 @@ private void advancedUpdateConfigActionPerformed(java.awt.event.ActionEvent evt) private javax.swing.JToggleButton cancel; private javax.swing.JToggleButton checkUpdates; private javax.swing.JScrollPane clientFrame; + private javax.swing.JTable clientTable; private javax.swing.JLabel clientTunnelLabel; private javax.swing.JLabel downloadSpeedLabel; private javax.swing.JLabel downloadUsageLabel; @@ -657,6 +811,7 @@ private void advancedUpdateConfigActionPerformed(java.awt.event.ActionEvent evt) private javax.swing.JPanel networkPanel; private javax.swing.JToggleButton ok; private javax.swing.JScrollPane serverFrame; + private javax.swing.JTable serverTable; private javax.swing.JLabel serverTunnelLabel; private javax.swing.JTabbedPane settingsPanel; private javax.swing.JPanel speedPanel; diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/ServerTunnelWindow.form b/apps/desktopgui/src/net/i2p/desktopgui/gui/ServerTunnelWindow.form new file mode 100644 index 000000000..303f49a7f --- /dev/null +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/ServerTunnelWindow.form @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JFrameFormInfo"> + <Properties> + <Property name="defaultCloseOperation" type="int" value="3"/> + <Property name="name" type="java.lang.String" value="Form" noResource="true"/> + </Properties> + <SyntheticProperties> + <SyntheticProperty name="formSizePolicy" type="int" value="1"/> + </SyntheticProperties> + <AuxValues> + <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="2"/> + <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> + <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> + <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> + <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/> + </AuxValues> + + <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout"> + <Property name="useNullLayout" type="boolean" value="true"/> + </Layout> + <SubComponents> + <Component class="javax.swing.JLabel" name="jLabel1"> + <Properties> + <Property name="text" type="java.lang.String" resourceKey="jLabel1.text"/> + <Property name="name" type="java.lang.String" value="jLabel1" noResource="true"/> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription"> + <AbsoluteConstraints x="10" y="10" width="-1" height="-1"/> + </Constraint> + </Constraints> + </Component> + </SubComponents> +</Form> diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/ServerTunnelWindow.java b/apps/desktopgui/src/net/i2p/desktopgui/gui/ServerTunnelWindow.java new file mode 100644 index 000000000..765210148 --- /dev/null +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/ServerTunnelWindow.java @@ -0,0 +1,60 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +/* + * ServerTunnelWindow.java + * + * Created on 11-jun-2009, 14:55:53 + */ + +package net.i2p.desktopgui.gui; + +import java.awt.event.ActionListener; + +/** + * + * @author mathias + */ +public class ServerTunnelWindow extends javax.swing.JFrame { + + /** Creates new form ServerTunnelWindow */ + public ServerTunnelWindow(int tunnelNumber, ActionListener al) { + initComponents(); + this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + this.setSize(600, 600); + this.setLocationRelativeTo(null); + this.requestFocus(); + this.setVisible(true); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents + private void initComponents() { + + jLabel1 = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + setName("Form"); // NOI18N + getContentPane().setLayout(null); + + org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(net.i2p.desktopgui.desktopgui.Main.class).getContext().getResourceMap(ServerTunnelWindow.class); + jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N + jLabel1.setName("jLabel1"); // NOI18N + getContentPane().add(jLabel1); + jLabel1.setBounds(10, 10, 43, 17); + + pack(); + }// </editor-fold>//GEN-END:initComponents + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + // End of variables declaration//GEN-END:variables + +} diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/ClientTunnelWindow.properties b/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/ClientTunnelWindow.properties new file mode 100644 index 000000000..ca036e197 --- /dev/null +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/ClientTunnelWindow.properties @@ -0,0 +1,26 @@ +# To change this template, choose Tools | Templates +# and open the template in the editor. + +Form.title=Client Tunnel Configuration +tunnelNameLabel.text=Name: +tunnelTypeLabel.text=Type: +tunnelType.text=jLabel10 +tunnelName.text=jTextField1 +tunnelPortLabel.text=Port: +tunnelPort.text=jTextField2 +tunnelDestination.text=jTextField3 +tunnelDestinationLabel.text=Destination: +tunnelProfileLabel.text=Profile: +delayConnect.text=Delay connect +sharedClient.text=Shared client +autoStart.text=Auto start +tunnelDepthLabel.text=Tunnel depth: +depthVarianceLabel.text=Depth variance: +tunnelCountLabel.text=Tunnel count: +backupTunnelCountLabel.text=Backup tunnel count: +reduceIdle.text=Reduce tunnel count when idle +closeIdle.text=Close tunnels when idle +delayIdle.text=Delay opening of tunnels when idle +save.text=Save +cancel.text=Cancel +changeTunnelState.text=Start Tunnel diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/GeneralConfiguration.properties b/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/GeneralConfiguration.properties index c6af27a51..d53ac80d0 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/GeneralConfiguration.properties +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/GeneralConfiguration.properties @@ -13,7 +13,7 @@ downloadspeed.text=jTextField2 uploadgb.text=jTextField3 downloadgb.text=jTextField4 updateMethod.text=What is your preferred automatic update setting? -updateInform.text=Only inform about updates +updateInform.text=Only inform about updates (not advised) updateDownload.text=Download and verify update file, do not restart updateDownloadRestart.text=Download, verify and restart checkUpdates.text=Check for updates now @@ -21,9 +21,17 @@ updateNow.text=Update available: update now advancedUpdateConfig.text=Advanced update configuration clientTunnelLabel.text=Client tunnels: serverTunnelLabel.text=Server tunnels: -tunnelsExplanation.text=Tunnel explanation +tunnelsExplanation.text=Click on a tunnel to view and change its configuration. + Tunnel explanation uploadUsageLabel.text=Monthly usage: downloadUsageLabel.text=Monthly usage: gbUploadLabel.text=GB gbDownloadLabel.text=GB uploadDownloadExplanation.text=Explanation ... +clientTable.columnModel.title3=Status +clientTable.columnModel.title2=Address +clientTable.columnModel.title1=Type +clientTable.columnModel.title0=Name +serverTable.columnModel.title0=Name +serverTable.columnModel.title3=Title 4 +serverTable.columnModel.title2=Status +serverTable.columnModel.title1=Address diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/ServerTunnelWindow.properties b/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/ServerTunnelWindow.properties new file mode 100644 index 000000000..683f3caa7 --- /dev/null +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/ServerTunnelWindow.properties @@ -0,0 +1 @@ +jLabel1.text=Name: diff --git a/history.txt b/history.txt index 28b3be69d..0be304fcb 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,8 @@ +2009-06-17 Mathiasdm + * desktopgui: + - Added client and server tunnel view + (saving does not work yet) + 2009-06-17 zzz * PeerSelector: - Limit exploratory tunnels to connected peers when over From 1eb4473e9d7a4cbf85f832064c149c9ec6bee15a Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Fri, 19 Jun 2009 00:04:19 +0000 Subject: [PATCH 27/58] * News Fetcher: - Change default news URL, use it instead of the old one even if the old one is saved in the configuration, to assist in the transition --- .../i2p/router/web/ConfigUpdateHandler.java | 5 ++-- .../i2p/router/web/ConfigUpdateHelper.java | 26 +++++++++---------- .../src/net/i2p/router/web/NewsFetcher.java | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java index 1729a209b..9366591fc 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java @@ -19,7 +19,8 @@ public class ConfigUpdateHandler extends FormHandler { 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://complication.i2p/news.xml"; + public static final String OLD_DEFAULT_NEWS_URL = "http://complication.i2p/news.xml"; + public static final String DEFAULT_NEWS_URL = "http://echelon.i2p/i2p/news.xml"; public static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency"; public static final String DEFAULT_REFRESH_FREQUENCY = 24*60*60*1000 + ""; public static final String PROP_UPDATE_POLICY = "router.updatePolicy"; @@ -57,7 +58,7 @@ public class ConfigUpdateHandler extends FormHandler { } if ( (_newsURL != null) && (_newsURL.length() > 0) ) { - String oldURL = _context.router().getConfigSetting(PROP_NEWS_URL); + String oldURL = ConfigUpdateHelper.getNewsURL(_context); if ( (oldURL == null) || (!_newsURL.equals(oldURL)) ) { _context.router().setConfigSetting(PROP_NEWS_URL, _newsURL); addFormNotice("Updating news URL to " + _newsURL); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java index d0d243799..4aac71dff 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java @@ -1,5 +1,6 @@ package net.i2p.router.web; +import net.i2p.I2PAppContext; import net.i2p.crypto.TrustedUpdate; import net.i2p.data.DataHelper; import net.i2p.router.RouterContext; @@ -12,8 +13,14 @@ public class ConfigUpdateHelper extends HelperBase { } public String getNewsURL() { - String url = _context.getProperty(ConfigUpdateHandler.PROP_NEWS_URL); - if (url != null) + return getNewsURL(_context); + } + + /** hack to replace the old news location with the new one, even if they have saved + the update page at some point */ + public static String getNewsURL(I2PAppContext ctx) { + String url = ctx.getProperty(ConfigUpdateHandler.PROP_NEWS_URL); + if (url != null && !url.equals(ConfigUpdateHandler.OLD_DEFAULT_NEWS_URL)) return url; else return ConfigUpdateHandler.DEFAULT_NEWS_URL; @@ -26,18 +33,10 @@ public class ConfigUpdateHelper extends HelperBase { return ConfigUpdateHandler.DEFAULT_UPDATE_URL; } public String getProxyHost() { - String host = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST); - if (host != null) - return host; - else - return ConfigUpdateHandler.DEFAULT_PROXY_HOST; + return _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); } public String getProxyPort() { - String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT); - if (port != null) - return port; - else - return ConfigUpdateHandler.DEFAULT_PROXY_PORT; + return _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT); } public String getUpdateThroughProxy() { @@ -76,8 +75,7 @@ public class ConfigUpdateHelper extends HelperBase { } public String getUpdatePolicySelectBox() { - String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY); - if (policy == null) policy = ConfigUpdateHandler.DEFAULT_UPDATE_POLICY; + String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY, ConfigUpdateHandler.DEFAULT_UPDATE_POLICY); StringBuffer buf = new StringBuffer(256); buf.append("<select name=\"updatePolicy\">"); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java index 98fac325d..0eeaca32b 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java @@ -110,7 +110,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { } } public void fetchNews() { - String newsURL = _context.getProperty(ConfigUpdateHandler.PROP_NEWS_URL, ConfigUpdateHandler.DEFAULT_NEWS_URL); + String newsURL = ConfigUpdateHelper.getNewsURL(_context); boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue(); String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT); From 4622f369c399b3fd9c7e9675371bd0e6f94173f0 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 21 Jun 2009 00:00:39 +0000 Subject: [PATCH 28/58] Pluck of revision aad5b15235be51189b696e6cabb7cc04e1b5e89f from branch i2p.i2p.zzz.test --------------------------------------------------------------------------------------- post-0.7.4 cleanup --- .../src/net/i2p/router/StatisticsManager.java | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index f107c5e1e..9293a6234 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -87,14 +87,9 @@ public class StatisticsManager implements Service { if (_includePeerRankings) { long publishedUptime = _context.router().getUptime(); - boolean commentOutIn074 = RouterVersion.VERSION.equals("0.7.3"); // Don't publish these for first hour - if (publishedUptime > 62*60*1000) { - if (commentOutIn074) - includeThroughput(stats); - else - includeAverageThroughput(stats); - } + if (publishedUptime > 62*60*1000) + includeAverageThroughput(stats); //includeRate("router.invalidMessageTime", stats, new long[] { 10*60*1000 }); //includeRate("router.duplicateMessageId", stats, new long[] { 24*60*60*1000 }); //includeRate("tunnel.duplicateIV", stats, new long[] { 24*60*60*1000 }); @@ -245,27 +240,6 @@ public class StatisticsManager implements Service { stats.setProperty("stat_bandwidthReceiveBps.60m", str); } - private void includeThroughput(Properties stats) { - RateStat sendRate = _context.statManager().getRate("bw.sendRate"); - if (sendRate != null) { - if (_context.router().getUptime() > 60*60*1000) { - Rate r = sendRate.getRate(60*60*1000); - if (r != null) - stats.setProperty("stat_bandwidthSendBps.60m", num(r.getAverageValue()) + ';' + num(r.getExtremeAverageValue()) + ";0;0;"); - } - } - - RateStat recvRate = _context.statManager().getRate("bw.recvRate"); - if (recvRate != null) { - if (_context.router().getUptime() > 60*60*1000) { - Rate r = recvRate.getRate(60*60*1000); - if (r != null) - stats.setProperty("stat_bandwidthReceiveBps.60m", num(r.getAverageValue()) + ';' + num(r.getExtremeAverageValue()) + ";0;0;"); - } - } - } - - private String getPeriod(Rate rate) { return DataHelper.formatDuration(rate.getPeriod()); } private final String num(double num) { From 8e2df567d809c443cd4be73443a576f0345015ae Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 21 Jun 2009 00:00:54 +0000 Subject: [PATCH 29/58] Pluck of revision 546257906e9b26c3a35e38967ebcaa54098d969f from branch i2p.i2p.zzz.test --------------------------------------------------------------------------------------- * ReseedHandler: - check for upper case HREF to be compatible with apache indexes --- .../java/src/net/i2p/router/web/ReseedHandler.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java index e701ede99..ceb8a280e 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java @@ -137,11 +137,16 @@ public class ReseedHandler { * Fetch a directory listing and then up to 200 routerInfo files in the listing. * The listing must contain (exactly) strings that match: * href="routerInfo-{hash}.dat"> + * OR + * HREF="routerInfo-{hash}.dat"> * and then it fetches the files * {seedURL}routerInfo-{hash}.dat * after appending a '/' to seedURL if it doesn't have one. * Essentially this means that the seedURL must be a directory, it * can't end with 'index.html', for example. + * + * Jetty directory listings are not compatible, as they look like + * HREF="/full/path/to/routerInfo-... **/ private void reseedOne(String seedURL, boolean echoStatus) { @@ -165,8 +170,11 @@ public class ReseedHandler { int total = 0; while (total++ < 1000) { int start = content.indexOf("href=\"routerInfo-", cur); - if (start < 0) - break; + if (start < 0) { + start = content.indexOf("HREF=\"routerInfo-", cur); + if (start < 0) + break; + } int end = content.indexOf(".dat\">", start); String name = content.substring(start+"href=\"routerInfo-".length(), end); From ce92b6cb6677e81319973554b8642b0f40d50cb2 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 21 Jun 2009 00:01:02 +0000 Subject: [PATCH 30/58] Pluck of revision 0dc8693b99a852b0183ffc6cd06804878f37ec89 from branch i2p.i2p.zzz.test --------------------------------------------------------------------------------------- Treat 5.0.0.0/8 (Hamachi) as local --- router/java/src/net/i2p/router/transport/TransportImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java index 2dbd9af3a..e6e3c80e5 100644 --- a/router/java/src/net/i2p/router/transport/TransportImpl.java +++ b/router/java/src/net/i2p/router/transport/TransportImpl.java @@ -559,6 +559,7 @@ public abstract class TransportImpl implements Transport { if ((addr[0]&0xFF) >= 224) return false; // no multicast if ((addr[0]&0xFF) == 0) return false; if ( ((addr[0]&0xFF) == 169) && ((addr[1]&0xFF) == 254) ) return false; + if ((addr[0]&0xFF) == 5) return false; // Hamachi return true; // or at least possible to be true } else if (addr.length == 16) { return false; From 306b3017e437a2cebbd32a8fc63f3fd3bd8c510f Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 21 Jun 2009 00:01:14 +0000 Subject: [PATCH 31/58] Pluck of revision 493986d03c43bac019996ea0b10b8a6a40e92a32 from branch i2p.i2p.zzz.test --------------------------------------------------------------------------------------- * Browser Launch: Wait until the routerconsole is up before launching the browser --- .../src/net/i2p/apps/systray/UrlLauncher.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java index c7524054e..b62b8ae1d 100644 --- a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java +++ b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java @@ -12,9 +12,12 @@ package net.i2p.apps.systray; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.net.InetSocketAddress; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; +import java.net.Socket; +import java.net.SocketAddress; import java.net.URL; import net.i2p.util.ShellCommand; @@ -33,6 +36,56 @@ public class UrlLauncher { ShellCommand _shellCommand = new ShellCommand(); + private static final int WAIT_TIME = 5*1000; + private static final int MAX_WAIT_TIME = 5*60*1000; + private static final int MAX_TRIES = 99; + + /** + * Prevent bad user experience by waiting for the server to be there + * before launching the browser. + * @return success + */ + public boolean waitForServer(String urlString) { + URL url; + try { + url = new URL(urlString); + } catch (MalformedURLException e) { + return false; + } + String host = url.getHost(); + int port = url.getPort(); + if (port <= 0) { + port = url.getDefaultPort(); + if (port <= 0) + return false; + } + SocketAddress sa; + try { + sa = new InetSocketAddress(host, port); + } catch (IllegalArgumentException iae) { + return false; + } + long done = System.currentTimeMillis() + MAX_WAIT_TIME; + for (int i = 0; i < MAX_TRIES; i++) { + try { + Socket test = new Socket(); + // this will usually fail right away if it's going to fail since it's local + test.connect(sa, WAIT_TIME); + // it worked + try { + test.close(); + } catch (IOException ioe) {} + return true; + } catch (Exception e) {} + if (System.currentTimeMillis() > done) + break; + try { + Thread.sleep(WAIT_TIME); + } catch (InterruptedException ie) {} + } + return false; + } + /** * Discovers the operating system the installer is running under and tries * to launch the given URL using the default browser for that platform; if @@ -49,6 +102,7 @@ public class UrlLauncher { String osName = System.getProperty("os.name"); + waitForServer(url); if (validateUrlFormat(url)) { if (osName.toLowerCase().indexOf("mac") > -1) { if (osName.toLowerCase().startsWith("mac os x")) { @@ -141,6 +195,7 @@ public class UrlLauncher { */ public boolean openUrl(String url, String browser) throws Exception { + waitForServer(url); if (validateUrlFormat(url)) if (_shellCommand.executeSilentAndWaitTimed(browser + " " + url, 5)) return true; From bc38ca4f914bf2ae06777b7ccc9ae90cf136fc01 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 21 Jun 2009 00:03:59 +0000 Subject: [PATCH 32/58] Pluck of revision 45a25185236e38606e761060427ee8fa60144a8c from branch i2p.i2p.zzz.test --------------------------------------------------------------------------------------- * netdb.jsp: Add country chart at bottom, clean up version chart --- core/java/src/net/i2p/util/ObjectCounter.java | 42 ++++++++++++ .../src/net/i2p/router/CommSystemFacade.java | 1 + .../KademliaNetworkDatabaseFacade.java | 67 +++++++++++-------- .../transport/CommSystemFacadeImpl.java | 16 +++-- .../router/tunnel/pool/TunnelPoolManager.java | 38 +++-------- 5 files changed, 102 insertions(+), 62 deletions(-) create mode 100644 core/java/src/net/i2p/util/ObjectCounter.java diff --git a/core/java/src/net/i2p/util/ObjectCounter.java b/core/java/src/net/i2p/util/ObjectCounter.java new file mode 100644 index 000000000..19c92ae66 --- /dev/null +++ b/core/java/src/net/i2p/util/ObjectCounter.java @@ -0,0 +1,42 @@ +package net.i2p.util; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Count things. + * + * @author zzz + */ +public class ObjectCounter<K> { + private ConcurrentHashMap<K, Integer> _map; + public ObjectCounter() { + _map = new ConcurrentHashMap(); + } + /** + * Add one. + * Not perfectly concurrent, new AtomicInteger(1) would be better, + * at the cost of some object churn. + */ + public void increment(K h) { + Integer i = _map.putIfAbsent(h, Integer.valueOf(1)); + if (i != null) + _map.put(h, Integer.valueOf(i.intValue() + 1)); + } + /** + * @return current count + */ + public int count(K h) { + Integer i = _map.get(h); + if (i != null) + return i.intValue(); + return 0; + } + /** + * @return set of objects with counts > 0 + */ + public Set<K> objects() { + return _map.keySet(); + } +} + diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java index 436501324..c26d9efbf 100644 --- a/router/java/src/net/i2p/router/CommSystemFacade.java +++ b/router/java/src/net/i2p/router/CommSystemFacade.java @@ -63,6 +63,7 @@ public abstract class CommSystemFacade implements Service { public byte[] getIP(Hash dest) { return null; } public void queueLookup(byte[] ip) {} public String getCountry(Hash peer) { return null; } + public String getCountryName(String code) { return code; } public String renderPeerHTML(Hash peer) { return null; } /** diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index 31d6dc04b..5a8c2f641 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -10,12 +10,15 @@ package net.i2p.router.networkdb.kademlia; import java.io.IOException; import java.io.Writer; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -42,6 +45,7 @@ import net.i2p.router.networkdb.DatabaseStoreMessageHandler; import net.i2p.router.networkdb.PublishLocalRouterInfoJob; import net.i2p.router.peermanager.PeerProfile; import net.i2p.util.Log; +import net.i2p.util.ObjectCounter; /** * Kademlia based version of the network database @@ -999,8 +1003,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { out.write(buf.toString()); buf.setLength(0); - /* coreVersion to Map of routerVersion to Integer */ - Map versions = new TreeMap(); + ObjectCounter<String> versions = new ObjectCounter(); + ObjectCounter<String> countries = new ObjectCounter(); Set routers = new TreeSet(new RouterInfoComparator()); routers.addAll(getRouters()); @@ -1012,40 +1016,47 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { renderRouterInfo(buf, ri, false, full); out.write(buf.toString()); buf.setLength(0); - String coreVersion = ri.getOption("coreVersion"); String routerVersion = ri.getOption("router.version"); - if ( (coreVersion != null) && (routerVersion != null) ) { - Map routerVersions = (Map)versions.get(coreVersion); - if (routerVersions == null) { - routerVersions = new TreeMap(); - versions.put(coreVersion, routerVersions); - } - Integer val = (Integer)routerVersions.get(routerVersion); - if (val == null) - routerVersions.put(routerVersion, Integer.valueOf(1)); - else - routerVersions.put(routerVersion, Integer.valueOf(val.intValue() + 1)); - } + if (routerVersion != null) + versions.increment(routerVersion); + String country = _context.commSystem().getCountry(key); + if(country != null) + countries.increment(country); } } - if (versions.size() > 0) { + buf.append("<table border=\"0\" cellspacing=\"30\"><tr><td valign=\"top\">"); + List<String> versionList = new ArrayList(versions.objects()); + if (versionList.size() > 0) { + Collections.sort(versionList, Collections.reverseOrder()); buf.append("<table border=\"1\">\n"); - buf.append("<tr><td><b>Core version</b></td><td><b>Router version</b></td><td><b>Number</b></td></tr>\n"); - for (Iterator iter = versions.entrySet().iterator(); iter.hasNext(); ) { - Map.Entry entry = (Map.Entry)iter.next(); - String coreVersion = (String)entry.getKey(); - Map routerVersions = (Map)entry.getValue(); - for (Iterator routerIter = routerVersions.keySet().iterator(); routerIter.hasNext(); ) { - String routerVersion = (String)routerIter.next(); - Integer num = (Integer)routerVersions.get(routerVersion); - buf.append("<tr><td>").append(DataHelper.stripHTML(coreVersion)); - buf.append("</td><td>").append(DataHelper.stripHTML(routerVersion)); - buf.append("</td><td>").append(num.intValue()).append("</td></tr>\n"); - } + buf.append("<tr><th>Version</th><th>Count</th></tr>\n"); + for (String routerVersion : versionList) { + int num = versions.count(routerVersion); + buf.append("<tr><td>").append(DataHelper.stripHTML(routerVersion)); + buf.append("</td><td align=\"right\">").append(num).append("</td></tr>\n"); } buf.append("</table>\n"); } + buf.append("</td><td valign=\"top\">"); + out.write(buf.toString()); + buf.setLength(0); + + List<String> countryList = new ArrayList(countries.objects()); + if (countryList.size() > 0) { + Collections.sort(countryList); + buf.append("<table border=\"1\">\n"); + buf.append("<tr><th>Country</th><th>Count</th></tr>\n"); + for (String country : countryList) { + int num = countries.count(country); + buf.append("<tr><td><img alt=\"").append(country.toUpperCase()).append("\""); + buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> "); + buf.append(_context.commSystem().getCountryName(country)); + buf.append("</td><td align=\"right\">").append(num).append("</td></tr>\n"); + } + buf.append("</table>\n"); + } + buf.append("</td></tr></table>"); out.write(buf.toString()); out.flush(); } diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index 528337239..03d3071bb 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -435,6 +435,16 @@ public class CommSystemFacadeImpl extends CommSystemFacade { return props.getProperty("host"); } + /** full name for a country code, or the code if we don't know the name */ + public String getCountryName(String c) { + if (_geoIP == null) + return c; + String n = _geoIP.fullName(c); + if (n == null) + return c; + return n; + } + /** Provide a consistent "look" for displaying router IDs in the console */ public String renderPeerHTML(Hash peer) { String h = peer.toBase64().substring(0, 4); @@ -442,11 +452,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade { String c = getCountry(peer); if (c != null) { buf.append("<img alt=\"").append(c.toUpperCase()).append("\" title=\""); - String n = _geoIP.fullName(c); - if (n != null) - buf.append(n); - else - buf.append(c); + buf.append(getCountryName(c)); buf.append("\" src=\"/flags.jsp?c=").append(c).append("\"> "); } buf.append("<tt><font size=\"+1\">"); diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index 9845a72f3..7293b6a1e 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -29,6 +29,7 @@ import net.i2p.router.tunnel.HopConfig; import net.i2p.stat.RateStat; import net.i2p.util.I2PThread; import net.i2p.util.Log; +import net.i2p.util.ObjectCounter; /** * @@ -588,15 +589,15 @@ public class TunnelPoolManager implements TunnelManagerFacade { private void renderPeers(Writer out) throws IOException { // count up the peers in the local pools - HashCounter lc = new HashCounter(); + ObjectCounter<Hash> lc = new ObjectCounter(); int tunnelCount = countTunnelsPerPeer(lc); // count up the peers in the participating tunnels - HashCounter pc = new HashCounter(); + ObjectCounter<Hash> pc = new ObjectCounter(); int partCount = countParticipatingPerPeer(pc); - Set<Hash> peers = new HashSet(lc.hashes()); - peers.addAll(pc.hashes()); + Set<Hash> peers = new HashSet(lc.objects()); + peers.addAll(pc.objects()); List<Hash> peerList = new ArrayList(peers); Collections.sort(peerList, new HashComparator()); @@ -625,7 +626,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { } /** @return total number of non-fallback expl. + client tunnels */ - private int countTunnelsPerPeer(HashCounter lc) { + private int countTunnelsPerPeer(ObjectCounter<Hash> lc) { List<TunnelPool> pools = new ArrayList(); listPools(pools); int tunnelCount = 0; @@ -661,12 +662,12 @@ public class TunnelPoolManager implements TunnelManagerFacade { * @return Set of peers that should not be allowed in another tunnel */ public Set<Hash> selectPeersInTooManyTunnels() { - HashCounter lc = new HashCounter(); + ObjectCounter<Hash> lc = new ObjectCounter(); int tunnelCount = countTunnelsPerPeer(lc); Set<Hash> rv = new HashSet(); if (tunnelCount >= 4 && _context.router().getUptime() > 10*60*1000) { int max = _context.getProperty("router.maxTunnelPercentage", DEFAULT_MAX_PCT_TUNNELS); - for (Hash h : lc.hashes()) { + for (Hash h : lc.objects()) { if (lc.count(h) > 0 && (lc.count(h) + 1) * 100 / (tunnelCount + 1) > max) rv.add(h); } @@ -675,7 +676,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { } /** @return total number of part. tunnels */ - private int countParticipatingPerPeer(HashCounter pc) { + private int countParticipatingPerPeer(ObjectCounter<Hash> pc) { List<HopConfig> participating = _context.tunnelDispatcher().listParticipatingTunnels(); for (HopConfig cfg : participating) { Hash from = cfg.getReceiveFrom(); @@ -694,27 +695,6 @@ public class TunnelPoolManager implements TunnelManagerFacade { } } - private static class HashCounter { - private ConcurrentHashMap<Hash, Integer> _map; - public HashCounter() { - _map = new ConcurrentHashMap(); - } - public void increment(Hash h) { - Integer i = _map.putIfAbsent(h, Integer.valueOf(1)); - if (i != null) - _map.put(h, Integer.valueOf(i.intValue() + 1)); - } - public int count(Hash h) { - Integer i = _map.get(h); - if (i != null) - return i.intValue(); - return 0; - } - public Set<Hash> hashes() { - return _map.keySet(); - } - } - private String getCapacity(Hash peer) { RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); if (info != null) { From 4a6d0e4ba20133d3a41a93ee9a5792633bf5df08 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 21 Jun 2009 00:06:54 +0000 Subject: [PATCH 33/58] Fix wrapper.config installs on windows --- installer/install.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer/install.xml b/installer/install.xml index 1d3d09fed..d51bd7f2a 100644 --- a/installer/install.xml +++ b/installer/install.xml @@ -106,7 +106,7 @@ http://www.javalobby.org/forums/thread.jspa?threadID=15967&tstart=0 and the izpack docs for some guidance. --> - <parsable targetfile="$INSTALL_PATH/wrapper.config" type="javaprop" /> + <parsable targetfile="$INSTALL_PATH/wrapper.config" type="plain" /> <parsable targetfile="$INSTALL_PATH/i2prouter" type="shell" os="unix|mac" /> <!-- postinstall stuff for windows --> From 9792336b337da36a7001acac973730d3e85fb48c Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 21 Jun 2009 00:23:14 +0000 Subject: [PATCH 34/58] -3 --- history.txt | 12 ++++++++++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index 0be304fcb..e88deb36a 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,15 @@ +2009-06-21 zzz + * Browser Launch: Wait until the routerconsole is up before launching the browser + * Installer: Fix wrapper.config parsing on windows + * netdb.jsp: Add country chart at bottom, clean up version chart + * News Fetcher: + - Change default news URL, use it instead of the old one even if + the old one is saved in the configuration, to assist in the transition + * ReseedHandler: + - check for upper case HREF to be compatible with apache indexes + * Statistics Manager: post-0.7.4 cleanup + * Transport: Treat 5.0.0.0/8 (Hamachi) as local + 2009-06-17 Mathiasdm * desktopgui: - Added client and server tunnel view diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index bd644cee5..ef84e9e97 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 2; + public final static long BUILD = 3; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; From abc83362e767950ef5dc0c3ecbdbfcc5ac6dd1da Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Mon, 22 Jun 2009 12:57:38 +0000 Subject: [PATCH 35/58] Try again to prevent two EventPumpers --- .../java/src/net/i2p/router/transport/ntcp/EventPumper.java | 6 +++--- .../src/net/i2p/router/transport/ntcp/NTCPTransport.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java index 3f9e5acfc..89c12250c 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java +++ b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java @@ -31,7 +31,7 @@ import net.i2p.util.Log; public class EventPumper implements Runnable { private RouterContext _context; private Log _log; - private boolean _alive; + private volatile boolean _alive; private Selector _selector; private final List _bufCache; private final List _wantsRead = new ArrayList(16); @@ -64,7 +64,7 @@ public class EventPumper implements Runnable { _expireIdleWriteTime = MAX_EXPIRE_IDLE_TIME; } - public void startPumping() { + public synchronized void startPumping() { if (_log.shouldLog(Log.INFO)) _log.info("Starting pumper"); // _wantsRead = new ArrayList(16); @@ -83,7 +83,7 @@ public class EventPumper implements Runnable { } } - public void stopPumping() { + public synchronized void stopPumping() { _alive = false; if (_selector != null && _selector.isOpen()) _selector.wakeup(); diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index 9f6b8e7ca..84cd76732 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -416,7 +416,7 @@ public class NTCPTransport extends TransportImpl { private static final int NUM_CONCURRENT_READERS = 3; private static final int NUM_CONCURRENT_WRITERS = 3; - public RouterAddress startListening() { + public synchronized RouterAddress startListening() { if (_log.shouldLog(Log.DEBUG)) _log.debug("Starting ntcp transport listening"); _finisher.start(); _pumper.startPumping(); @@ -428,7 +428,7 @@ public class NTCPTransport extends TransportImpl { return bindAddress(); } - public RouterAddress restartListening(RouterAddress addr) { + public synchronized RouterAddress restartListening(RouterAddress addr) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Restarting ntcp transport listening"); _finisher.start(); _pumper.startPumping(); @@ -602,7 +602,7 @@ public class NTCPTransport extends TransportImpl { * This doesn't (completely) block, caller should check isAlive() * before calling startListening() or restartListening() */ - public void stopListening() { + public synchronized void stopListening() { if (_log.shouldLog(Log.DEBUG)) _log.debug("Stopping ntcp transport"); _pumper.stopPumping(); _writer.stopWriting(); From 1fd5a20373869e1c57f795264c32e2ac4f935dbb Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Mon, 22 Jun 2009 14:07:31 +0000 Subject: [PATCH 36/58] Include console.css in new installs! --- build.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.xml b/build.xml index ae6224a70..b80f6fd12 100644 --- a/build.xml +++ b/build.xml @@ -297,6 +297,9 @@ <copy todir="pkg-temp/docs/themes/" > <fileset dir="installer/resources/themes/" /> </copy> + <!-- CSS now in docs/, not in the .war --> + <copy file="apps/routerconsole/jsp/default.css" tofile="pkg-temp/docs/themes/console/console.css" /> + <copy file="apps/routerconsole/jsp/default.css" tofile="pkg-temp/docs/themes/console/defCon1/console.css" /> <mkdir dir="pkg-temp/eepsite" /> <mkdir dir="pkg-temp/eepsite/webapps" /> <mkdir dir="pkg-temp/eepsite/logs" /> From 917e1023e4ec37eabcd6e4be6eca5547ca63f970 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Mon, 22 Jun 2009 14:16:52 +0000 Subject: [PATCH 37/58] Catch AIOOBE reported by tuna http://zzz.i2p/topics/332 --- router/java/src/org/cybergarage/upnp/ControlPoint.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/router/java/src/org/cybergarage/upnp/ControlPoint.java b/router/java/src/org/cybergarage/upnp/ControlPoint.java index f1d56f44b..d66209f97 100644 --- a/router/java/src/org/cybergarage/upnp/ControlPoint.java +++ b/router/java/src/org/cybergarage/upnp/ControlPoint.java @@ -276,7 +276,14 @@ public class ControlPoint implements HTTPRequestListener { int nRoots = devNodeList.size(); for (int n=0; n<nRoots; n++) { - Node rootNode = devNodeList.getNode(n); + // AIOOB was thrown from here, maybe would be better to + // copy the list before traversal? + Node rootNode; + try { + rootNode = devNodeList.getNode(n); + } catch (ArrayIndexOutOfBoundsException aioob) { + break; + } Device dev = getDevice(rootNode); if (dev == null) continue; From 29d0c19b0ea1eeca20ce3f379933e8372dd28b1f Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Mon, 22 Jun 2009 14:50:59 +0000 Subject: [PATCH 38/58] Update: increase max retries --- .../java/src/net/i2p/router/web/UpdateHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java index 35edadfb6..fc0a2177d 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java @@ -137,7 +137,8 @@ public class UpdateHandler { try { EepGet get = null; if (shouldProxy) - get = new EepGet(_context, proxyHost, proxyPort, 20, SIGNED_UPDATE_FILE, updateURL, false); + // 40 retries!! + get = new EepGet(_context, proxyHost, proxyPort, 40, SIGNED_UPDATE_FILE, updateURL, false); else get = new EepGet(_context, 1, SIGNED_UPDATE_FILE, updateURL, false); get.addStatusListener(UpdateRunner.this); From 5d0d7aca58943a77bfdfca1bed9d1919b1f6385b Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Mon, 22 Jun 2009 17:01:56 +0000 Subject: [PATCH 39/58] minor cleanup --- .../i2p/router/client/ClientListenerRunner.java | 2 +- .../i2p/router/client/ClientManagerFacadeImpl.java | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/router/java/src/net/i2p/router/client/ClientListenerRunner.java b/router/java/src/net/i2p/router/client/ClientListenerRunner.java index 38105e9c9..0f26b8d00 100644 --- a/router/java/src/net/i2p/router/client/ClientListenerRunner.java +++ b/router/java/src/net/i2p/router/client/ClientListenerRunner.java @@ -43,7 +43,7 @@ public class ClientListenerRunner implements Runnable { _running = false; _listening = false; - String val = context.getProperty(BIND_ALL_INTERFACES, "False"); + String val = context.getProperty(BIND_ALL_INTERFACES); _bindAllInterfaces = Boolean.valueOf(val).booleanValue(); } diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java index 543b5e29c..9e706beda 100644 --- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java +++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java @@ -49,18 +49,8 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade { public void startup() { _log.info("Starting up the client subsystem"); - String portStr = _context.router().getConfigSetting(PROP_CLIENT_PORT); - if (portStr != null) { - try { - int port = Integer.parseInt(portStr); - _manager = new ClientManager(_context, port); - } catch (NumberFormatException nfe) { - _log.error("Error setting the port: " + portStr + " is not valid", nfe); - _manager = new ClientManager(_context, DEFAULT_PORT); - } - } else { - _manager = new ClientManager(_context, DEFAULT_PORT); - } + int port = _context.getProperty(PROP_CLIENT_PORT, DEFAULT_PORT); + _manager = new ClientManager(_context, port); } public void shutdown() { From 66993665972e237dde89a55e181d17fb9d52f7f9 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Mon, 22 Jun 2009 19:56:03 +0000 Subject: [PATCH 40/58] * Browser Launch: Add sensible-browser, x-www-browser, defaultbrowser, and www-browser in an attempt to launch the user's preferred browser --- .../src/net/i2p/apps/systray/UrlLauncher.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java index b62b8ae1d..7cce9e59e 100644 --- a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java +++ b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java @@ -155,6 +155,20 @@ public class UrlLauncher { // fall through } + // This debian script tries everything in $BROWSER, then gnome-www-browser and x-www-browser + // if X is running and www-browser otherwise. Those point to the user's preferred + // browser using the update-alternatives system. + if (_shellCommand.executeSilentAndWaitTimed("sensible-browser " + url, 5)) + return true; + + // Try x-www-browser directly + if (_shellCommand.executeSilentAndWaitTimed("x-www-browser " + url, 5)) + return true; + + // puppy linux + if (_shellCommand.executeSilentAndWaitTimed("defaultbrowser " + url, 5)) + return true; + if (_shellCommand.executeSilentAndWaitTimed("opera -newpage " + url, 5)) return true; @@ -173,6 +187,10 @@ public class UrlLauncher { if (_shellCommand.executeSilentAndWaitTimed("galeon " + url, 5)) return true; + // Text Mode Browsers only below here + if (_shellCommand.executeSilentAndWaitTimed("www-browser " + url, 5)) + return true; + if (_shellCommand.executeSilentAndWaitTimed("links " + url, 5)) return true; From 1c9d84771ed071cf32e645d5d5f038936c3cf98d Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Tue, 23 Jun 2009 01:35:55 +0000 Subject: [PATCH 41/58] configupdate.jsp cleanup --- apps/routerconsole/jsp/configupdate.jsp | 37 ++++++++++++++----------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/routerconsole/jsp/configupdate.jsp b/apps/routerconsole/jsp/configupdate.jsp index 433cea665..043541275 100644 --- a/apps/routerconsole/jsp/configupdate.jsp +++ b/apps/routerconsole/jsp/configupdate.jsp @@ -27,27 +27,32 @@ 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")%>" /> +<table border="0" cellspacing="5"><tr><td valign="top"><b>News:</b><td> <% if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"))) { %> <i>Update In Progress</i><br /><br /> <% } else { %> <input type="submit" name="action" value="Check for update now" /><br /><br /> <% } %> - News URL: - <input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"><br /> - Refresh frequency: - <jsp:getProperty name="updatehelper" property="refreshFrequencySelectBox" /><br /> - Update policy: - <jsp:getProperty name="updatehelper" property="updatePolicySelectBox" /><br /> - <p>Update through the eepProxy? - <jsp:getProperty name="updatehelper" property="updateThroughProxy" /><br /> - eepProxy host: <input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /><br /> - eepProxy port: <input type="text" size="4" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /></p> - <p>Update URLs:<br /> - <textarea name="updateURL" cols="90" rows="4" wrap="off"><jsp:getProperty name="updatehelper" property="updateURL" /></textarea></p> - <p>Trusted keys:</br /> - <textarea name="trustedKeys" cols="90" rows="4" wrap="off"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea></p> - <br /> - <input type="submit" name="action" value="Save" /> +<tr><td><td> +<tr><td valign="top"><b>News URL:</b> +<td><input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"> +<tr><td valign="top"><b>Refresh frequency:</b> +<td><jsp:getProperty name="updatehelper" property="refreshFrequencySelectBox" /> +<tr><td valign="top"><b>Update policy:</b> +<td><jsp:getProperty name="updatehelper" property="updatePolicySelectBox" /> +<tr><td valign="top"><b>Update through the eepProxy?</b> +<td><jsp:getProperty name="updatehelper" property="updateThroughProxy" /> +<tr><td valign="top"><b>eepProxy host:</b> +<td><input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /> +<tr><td valign="top"><b>eepProxy port:</b> +<td><input type="text" size="4" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /> +<tr><td valign="top"><b>Update URLs:</b> +<td><textarea name="updateURL" cols="90" rows="4" wrap="off"><jsp:getProperty name="updatehelper" property="updateURL" /></textarea> +<tr><td valign="top"><b>Trusted keys:</b> +<td><textarea name="trustedKeys" cols="90" rows="4" wrap="off"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea> +<tr><td><td> +<tr><td><td><input type="submit" name="action" value="Save" /> <input type="reset" value="Cancel" /> +</table> </form> </div> From b93aada21312b5b3c25e28e84604f532f98d131c Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Tue, 23 Jun 2009 15:59:30 +0000 Subject: [PATCH 42/58] -4 --- history.txt | 9 +++++++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index e88deb36a..d34c0e94e 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,12 @@ +2009-06-23 zzz + * Browser Launch: Add sensible-browser, x-www-browser, defaultbrowser, and + www-browser in an attempt to launch the user's preferred browser + * configupdate.jsp: Cleanup + * Installer: Include console.css!!! + * NTCP: Try again to prevent two Event Pumpers + * Update: Increase max retries + * UPnP: Catch AIOOBE reported by tuna + 2009-06-21 zzz * Browser Launch: Wait until the routerconsole is up before launching the browser * Installer: Fix wrapper.config parsing on windows diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index ef84e9e97..e4d51a2bb 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 3; + public final static long BUILD = 4; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; From 47cd9beefae87102c08280e4a384d5c75a459c26 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Wed, 24 Jun 2009 18:47:17 +0000 Subject: [PATCH 43/58] Change table headings to th tags to prep for css changes --- .../i2p/router/web/ConfigClientsHelper.java | 4 +- .../i2p/router/web/ConfigTunnelsHelper.java | 14 +++--- router/java/src/net/i2p/router/JobQueue.java | 6 +-- .../peermanager/ProfileOrganizerRenderer.java | 46 +++++++++---------- .../router/transport/udp/UDPTransport.java | 44 +++++++++--------- .../router/tunnel/pool/TunnelPoolManager.java | 8 ++-- 6 files changed, 61 insertions(+), 61 deletions(-) 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 984569b96..7acccdc49 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java @@ -15,7 +15,7 @@ public class ConfigClientsHelper extends HelperBase { public String getForm1() { StringBuffer buf = new StringBuffer(1024); buf.append("<table border=\"1\">\n"); - buf.append("<tr><td>Client</td><td>Run at Startup?</td><td>Start Now</td><td>Class and arguments</td></tr>\n"); + buf.append("<tr><th>Client</th><th>Run at Startup?</th><th>Start Now</th><th>Class and arguments</th></tr>\n"); List clients = ClientAppConfig.getClientApps(_context); for (int cur = 0; cur < clients.size(); cur++) { @@ -31,7 +31,7 @@ public class ConfigClientsHelper extends HelperBase { public String getForm2() { StringBuffer buf = new StringBuffer(1024); buf.append("<table border=\"1\">\n"); - buf.append("<tr><td>WebApp</td><td>Run at Startup?</td><td>Start Now</td><td>Description</td></tr>\n"); + buf.append("<tr><th>WebApp</th><th>Run at Startup?</th><th>Start Now</th><th>Description</th></tr>\n"); Properties props = RouterConsoleRunner.webAppProperties(); Set keys = new TreeSet(props.keySet()); for (Iterator iter = keys.iterator(); iter.hasNext(); ) { 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 2e53be6b5..f1a63e421 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java @@ -56,27 +56,27 @@ public class ConfigTunnelsHelper extends HelperBase { private static final int MIN_NEG_VARIANCE = -1; private void renderForm(StringBuffer buf, int index, String prefix, String name, TunnelPoolSettings in, TunnelPoolSettings out) { - buf.append("<tr><td colspan=\"3\"><b><a name=\"").append(prefix).append("\">"); - buf.append(name).append("</a></b></td></tr>\n"); + buf.append("<tr><th colspan=\"3\"><a name=\"").append(prefix).append("\">"); + buf.append(name).append("</a></th></tr>\n"); if (in.getLength() <= 0 || in.getLength() + in.getLengthVariance() <= 0 || out.getLength() <= 0 || out.getLength() + out.getLengthVariance() <= 0) - buf.append("<tr><td colspan=\"3\"><font color=\"red\">ANONYMITY WARNING - Settings include 0-hop tunnels</font></td></tr>"); + buf.append("<tr><th colspan=\"3\"><font color=\"red\">ANONYMITY WARNING - Settings include 0-hop tunnels</font></th></tr>"); else if (in.getLength() <= 1 || in.getLength() + in.getLengthVariance() <= 1 || out.getLength() <= 1 || out.getLength() + out.getLengthVariance() <= 1) - buf.append("<tr><td colspan=\"3\"><font color=\"red\">ANONYMITY WARNING - Settings include 1-hop tunnels</font></td></tr>"); + buf.append("<tr><th colspan=\"3\"><font color=\"red\">ANONYMITY WARNING - Settings include 1-hop tunnels</font></th></tr>"); if (in.getLength() + Math.abs(in.getLengthVariance()) >= WARN_LENGTH || out.getLength() + Math.abs(out.getLengthVariance()) >= WARN_LENGTH) - buf.append("<tr><td colspan=\"3\"><font color=\"red\">PERFORMANCE WARNING - Settings include very long tunnels</font></td></tr>"); + buf.append("<tr><th colspan=\"3\"><font color=\"red\">PERFORMANCE WARNING - Settings include very long tunnels</font></th></tr>"); if (in.getQuantity() + in.getBackupQuantity() >= WARN_QUANTITY || out.getQuantity() + out.getBackupQuantity() >= WARN_QUANTITY) - buf.append("<tr><td colspan=\"3\"><font color=\"red\">PERFORMANCE WARNING - Settings include high tunnel quantities</font></td></tr>"); + buf.append("<tr><th colspan=\"3\"><font color=\"red\">PERFORMANCE WARNING - Settings include high tunnel quantities</font></th></tr>"); - buf.append("<tr><td></td><td><b>Inbound</b></td><td><b>Outbound</b></td></tr>\n"); + buf.append("<tr><th></th><th>Inbound</th><th>Outbound</th></tr>\n"); // tunnel depth buf.append("<tr><td>Depth</td>\n"); diff --git a/router/java/src/net/i2p/router/JobQueue.java b/router/java/src/net/i2p/router/JobQueue.java index 8ec40f56e..890b303c0 100644 --- a/router/java/src/net/i2p/router/JobQueue.java +++ b/router/java/src/net/i2p/router/JobQueue.java @@ -669,9 +669,9 @@ public class JobQueue { /** render the HTML for the job stats */ private void getJobStats(StringBuffer buf) { buf.append("<table border=\"1\">\n"); - buf.append("<tr><td><b>Job</b></td><td><b>Runs</b></td>"); - buf.append("<td><b>Time</b></td><td><b><i>Avg</i></b></td><td><b><i>Max</i></b></td><td><b><i>Min</i></b></td>"); - buf.append("<td><b>Pending</b></td><td><b><i>Avg</i></b></td><td><b><i>Max</i></b></td><td><b><i>Min</i></b></td></tr>\n"); + buf.append("<tr><th>Job</th><th>Runs</th>"); + buf.append("<th>Time</th><th><i>Avg</i></th><th><i>Max</i></th><th><i>Min</i></th>"); + buf.append("<th>Pending</th><th><i>Avg</i></th><th><i>Max</i></th><th><i>Min</i></th></tr>\n"); long totRuns = 0; long totExecTime = 0; long avgExecTime = 0; diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java index 0aff93485..20806cca7 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java @@ -61,13 +61,13 @@ class ProfileOrganizerRenderer { buf.append("<p>Showing ").append(order.size()).append(" recent profiles, hiding ").append(peers.size()-order.size()).append(" older profiles</p>"); buf.append("<table border=\"1\">"); buf.append("<tr>"); - buf.append("<td><b>Peer</b></td>"); - buf.append("<td><b>Groups (Caps)</b></td>"); - buf.append("<td><b>Speed</b></td>"); - buf.append("<td><b>Capacity</b></td>"); - buf.append("<td><b>Integration</b></td>"); - buf.append("<td><b>Failing?</b></td>"); - buf.append("<td> </td>"); + buf.append("<th>Peer</th>"); + buf.append("<th>Groups (Caps)</th>"); + buf.append("<th>Speed</th>"); + buf.append("<th>Capacity</th>"); + buf.append("<th>Integration</th>"); + buf.append("<th>Failing?</th>"); + buf.append("<th> </th>"); buf.append("</tr>"); int prevTier = 1; for (Iterator iter = order.iterator(); iter.hasNext();) { @@ -159,22 +159,22 @@ class ProfileOrganizerRenderer { buf.append("<h2>Floodfill and Integrated Peers</h2>\n"); buf.append("<table border=\"1\">"); buf.append("<tr>"); - buf.append("<td><b>Peer</b></td>"); - buf.append("<td><b>Caps</b></td>"); - buf.append("<td><b>Integ. Value</b></td>"); - buf.append("<td><b>Last Heard About</b></td>"); - buf.append("<td><b>Last Heard From</b></td>"); - buf.append("<td><b>Last Successful Send</b></td>"); - buf.append("<td><b>Last Failed Send</b></td>"); - buf.append("<td><b>10m Resp. Time</b></td>"); - buf.append("<td><b>1h Resp. Time</b></td>"); - buf.append("<td><b>1d Resp. Time</b></td>"); - buf.append("<td><b>Successful Lookups</b></td>"); - buf.append("<td><b>Failed Lookups</b></td>"); - buf.append("<td><b>New Stores</b></td>"); - buf.append("<td><b>Old Stores</b></td>"); - buf.append("<td><b>1h Fail Rate</b></td>"); - buf.append("<td><b>1d Fail Rate</b></td>"); + buf.append("<th>Peer</th>"); + buf.append("<th>Caps</th>"); + buf.append("<th>Integ. Value</th>"); + buf.append("<th>Last Heard About</th>"); + buf.append("<th>Last Heard From</th>"); + buf.append("<th>Last Successful Send</th>"); + buf.append("<th>Last Failed Send</th>"); + buf.append("<th>10m Resp. Time</th>"); + buf.append("<th>1h Resp. Time</th>"); + buf.append("<th>1d Resp. Time</th>"); + buf.append("<th>Successful Lookups</th>"); + buf.append("<th>Failed Lookups</th>"); + buf.append("<th>New Stores</th>"); + buf.append("<th>Old Stores</th>"); + buf.append("<th>1h Fail Rate</th>"); + buf.append("<th>1d Fail Rate</th>"); buf.append("</tr>"); for (Iterator iter = integratedPeers.iterator(); iter.hasNext();) { PeerProfile prof = (PeerProfile)iter.next(); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index b42efbfae..e010e5213 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -1769,48 +1769,48 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority buf.append(" timeout: ").append(DataHelper.formatDuration(_expireTimeout)); buf.append("</b><br />\n"); buf.append("<table border=\"1\">\n"); - buf.append("<tr><td><b><a href=\"#def.peer\">Peer</a></b>"); + buf.append("<tr><th><a href=\"#def.peer\">Peer</a>"); if (sortFlags != FLAG_ALPHA) buf.append(" <a href=\"").append(urlBase).append("?sort=0\">V</a> "); - buf.append("</td><td><b><a href=\"#def.dir\">Dir/Intro</a></b></td><td><b><a href=\"#def.idle\">Idle</a></b>"); + buf.append("</th><th><a href=\"#def.dir\">Dir/Intro</a></th><th><a href=\"#def.idle\">Idle</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by idle inbound", FLAG_IDLE_IN); buf.append("/"); appendSortLinks(buf, urlBase, sortFlags, "Sort by idle outbound", FLAG_IDLE_OUT); - buf.append("</td>"); - buf.append("<td><b><a href=\"#def.rate\">In/Out</a></b>"); + buf.append("</th>"); + buf.append("<th><a href=\"#def.rate\">In/Out</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by inbound rate", FLAG_RATE_IN); buf.append("/"); appendSortLinks(buf, urlBase, sortFlags, "Sort by outbound rate", FLAG_RATE_OUT); - buf.append("</td>\n"); - buf.append("<td><b><a href=\"#def.up\">Up</a></b>"); + buf.append("</th>\n"); + buf.append("<th><a href=\"#def.up\">Up</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by connection uptime", FLAG_UPTIME); - buf.append("</td><td><b><a href=\"#def.skew\">skew</a></b>"); + buf.append("</th><th><a href=\"#def.skew\">skew</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by clock skew", FLAG_SKEW); - buf.append("</td>\n"); - buf.append("<td><b><a href=\"#def.cwnd\">Cwnd</a></b>"); + buf.append("</th>\n"); + buf.append("<th><a href=\"#def.cwnd\">Cwnd</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by congestion window", FLAG_CWND); - buf.append("</td><td><b><a href=\"#def.ssthresh\">Ssthresh</a></b>"); + buf.append("</th><th><a href=\"#def.ssthresh\">Ssthresh</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by slow start threshold", FLAG_SSTHRESH); - buf.append("</td>\n"); - buf.append("<td><b><a href=\"#def.rtt\">Rtt</a></b>"); + buf.append("</th>\n"); + buf.append("<th><a href=\"#def.rtt\">Rtt</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by round trip time", FLAG_RTT); - buf.append("</td><td><b><a href=\"#def.dev\">Dev</a></b>"); + buf.append("</th><th><a href=\"#def.dev\">Dev</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by round trip time deviation", FLAG_DEV); - buf.append("</td><td><b><a href=\"#def.rto\">Rto</a></b>"); + buf.append("</th><th><a href=\"#def.rto\">Rto</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by retransmission timeout", FLAG_RTO); - buf.append("</td>\n"); - buf.append("<td><b><a href=\"#def.mtu\">Mtu</a></b>"); + buf.append("</th>\n"); + buf.append("<th><a href=\"#def.mtu\">Mtu</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by maximum transmit unit", FLAG_MTU); - buf.append("</td><td><b><a href=\"#def.send\">Send</a></b>"); + buf.append("</th><th><a href=\"#def.send\">Send</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by packets sent", FLAG_SEND); - buf.append("</td><td><b><a href=\"#def.recv\">Recv</a></b>"); + buf.append("</th><th><a href=\"#def.recv\">Recv</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by packets received", FLAG_RECV); - buf.append("</td>\n"); - buf.append("<td><b><a href=\"#def.resent\">Resent</a></b>"); + buf.append("</th>\n"); + buf.append("<th><a href=\"#def.resent\">Resent</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by packets retransmitted", FLAG_RESEND); - buf.append("</td><td><b><a href=\"#def.dupRecv\">DupRecv</a></b>"); + buf.append("</th><th><a href=\"#def.dupRecv\">DupRecv</a>"); appendSortLinks(buf, urlBase, sortFlags, "Sort by packets received more than once", FLAG_DUP); - buf.append("</td>\n"); + buf.append("</th>\n"); buf.append("</tr>\n"); out.write(buf.toString()); buf.setLength(0); diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index 7293b6a1e..49f82acae 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -439,9 +439,9 @@ public class TunnelPoolManager implements TunnelManagerFacade { List participating = _context.tunnelDispatcher().listParticipatingTunnels(); Collections.sort(participating, new TunnelComparator()); out.write("<h2><a name=\"participating\">Participating tunnels</a>:</h2><table border=\"1\">\n"); - out.write("<tr><td><b>Receive on</b></td><td><b>From</b></td><td>" - + "<b>Send on</b></td><td><b>To</b></td><td><b>Expiration</b></td>" - + "<td><b>Usage</b></td><td><b>Rate</b></td><td><b>Role</b></td></tr>\n"); + out.write("<tr><th>Receive on</th><th>From</th><th>" + + "Send on</th><th>To</th><th>Expiration</th>" + + "<th>Usage</th><th>Rate</th><th>Role</th></tr>\n"); long processed = 0; RateStat rs = _context.statManager().getRate("tunnel.participatingMessageCount"); if (rs != null) @@ -602,7 +602,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { Collections.sort(peerList, new HashComparator()); out.write("<h2><a name=\"peers\">Tunnel Counts By Peer</a>:</h2>\n"); - out.write("<table border=\"1\"><tr><td><b>Peer</b></td><td><b>Expl. + Client</b></td><td><b>% of total</b></td><td><b>Part. from + to</b></td><td><b>% of total</b></td></tr>\n"); + out.write("<table border=\"1\"><tr><th>Peer</th><th>Expl. + Client</th><th>% of total</th><th>Part. from + to</th><th>% of total</th></tr>\n"); for (Hash h : peerList) { out.write("<tr><td align=\"right\">"); out.write(netDbLink(h)); From 56a700e82d9134c24b1867f57f53078ab7175df9 Mon Sep 17 00:00:00 2001 From: sponge <sponge@mail.i2p> Date: Thu, 25 Jun 2009 00:16:19 +0000 Subject: [PATCH 44/58] 2009-06-25 sponge * Summary frame layout change so it makes sense. --- .../java/src/net/i2p/router/web/SummaryHelper.java | 2 +- apps/routerconsole/jsp/summarynoframe.jsp | 6 +++--- history.txt | 3 +++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java index 0810dd950..7cf1965f1 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java @@ -365,7 +365,7 @@ public class SummaryHelper extends HelperBase { buf.append("<i>No leases</i><br />\n"); } } - buf.append("<hr />\n"); + // buf.append("<hr />\n"); return buf.toString(); } diff --git a/apps/routerconsole/jsp/summarynoframe.jsp b/apps/routerconsole/jsp/summarynoframe.jsp index f3f8b8002..c12426a85 100644 --- a/apps/routerconsole/jsp/summarynoframe.jsp +++ b/apps/routerconsole/jsp/summarynoframe.jsp @@ -83,8 +83,6 @@ <b>Used:</b> <jsp:getProperty name="helper" property="inboundTransferred" />/<jsp:getProperty name="helper" property="outboundTransferred" /><br /> <hr /> - <jsp:getProperty name="helper" property="destinations" /> - <u><b>Tunnels in/out</b></u><br /> <b>Exploratory:</b> <jsp:getProperty name="helper" property="inboundTunnels" />/<jsp:getProperty name="helper" property="outboundTunnels" /><br /> <b>Client:</b> <jsp:getProperty name="helper" property="inboundClientTunnels" />/<jsp:getProperty name="helper" property="outboundClientTunnels" /><br /> @@ -97,4 +95,6 @@ <b>Tunnel lag:</b> <jsp:getProperty name="helper" property="tunnelLag" /><br /> <b>Handle backlog:</b> <jsp:getProperty name="helper" property="inboundBacklog" /><br /> <b><jsp:getProperty name="helper" property="tunnelStatus" /></b><br /> - + <hr /> + + <jsp:getProperty name="helper" property="destinations" /> diff --git a/history.txt b/history.txt index d34c0e94e..33381eb02 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,6 @@ +2009-06-25 sponge + * Summary frame layout change so it makes sense. + 2009-06-23 zzz * Browser Launch: Add sensible-browser, x-www-browser, defaultbrowser, and www-browser in an attempt to launch the user's preferred browser diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index e4d51a2bb..b54d1de4b 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 4; + public final static long BUILD = 5; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; From 0606050231f4e5edc698b5b6a9540eefd5a9bc6e Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sat, 27 Jun 2009 01:08:40 +0000 Subject: [PATCH 45/58] * NetDb stats: Normalize tunnel build stats for increased anonymity, effective in 0.7.6 --- .../src/net/i2p/router/StatisticsManager.java | 61 +++++++++++++++++-- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index 9293a6234..df51e36b4 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -139,12 +139,18 @@ public class StatisticsManager implements Service { //includeRate("tunnel.buildRequestTime", stats, new long[] { 10*60*1000 }); long rate = 60*60*1000; - includeRate("tunnel.buildClientExpire", stats, new long[] { rate }); - includeRate("tunnel.buildClientReject", stats, new long[] { rate }); - includeRate("tunnel.buildClientSuccess", stats, new long[] { rate }); - includeRate("tunnel.buildExploratoryExpire", stats, new long[] { rate }); - includeRate("tunnel.buildExploratoryReject", stats, new long[] { rate }); - includeRate("tunnel.buildExploratorySuccess", stats, new long[] { rate }); + boolean commentOutIn076 = RouterVersion.VERSION.equals("0.7.5"); + if (commentOutIn076) { + includeRate("tunnel.buildClientExpire", stats, new long[] { rate }); + includeRate("tunnel.buildClientReject", stats, new long[] { rate }); + includeRate("tunnel.buildClientSuccess", stats, new long[] { rate }); + includeRate("tunnel.buildExploratoryExpire", stats, new long[] { rate }); + includeRate("tunnel.buildExploratoryReject", stats, new long[] { rate }); + includeRate("tunnel.buildExploratorySuccess", stats, new long[] { rate }); + } else { + includeTunnelRates("Client", stats, rate); + includeTunnelRates("Exploratory", stats, rate); + } //includeRate("tunnel.rejectTimeout", stats, new long[] { 10*60*1000 }); //includeRate("tunnel.rejectOverloaded", stats, new long[] { 10*60*1000 }); //includeRate("tunnel.acceptLoad", stats, new long[] { 10*60*1000 }); @@ -223,6 +229,49 @@ public class StatisticsManager implements Service { return buf.toString(); } + private static final String[] tunnelStats = { "Expire", "Reject", "Success" }; + + /** + * Add tunnel build rates with some mods to hide absolute quantities + * In particular, report counts normalized to 100 (i.e. a percentage) + */ + private void includeTunnelRates(String tunnelType, Properties stats, long selectedPeriod) { + long totalEvents = 0; + for (String tunnelStat : tunnelStats) { + String rateName = "tunnel.build" + tunnelType + tunnelStat; + RateStat stat = _context.statManager().getRate(rateName); + if (stat == null) continue; + Rate curRate = stat.getRate(selectedPeriod); + if (curRate == null) continue; + totalEvents += curRate.getLastEventCount(); + } + if (totalEvents <= 0) + return; + for (String tunnelStat : tunnelStats) { + String rateName = "tunnel.build" + tunnelType + tunnelStat; + RateStat stat = _context.statManager().getRate(rateName); + if (stat == null) continue; + Rate curRate = stat.getRate(selectedPeriod); + if (curRate == null) continue; + double fudgeQuantity = 100.0d * curRate.getLastEventCount() / totalEvents; + stats.setProperty("stat_" + rateName + '.' + getPeriod(curRate), renderRate(curRate, fudgeQuantity)); + } + } + + private String renderRate(Rate rate, double fudgeQuantity) { + StringBuffer buf = new StringBuffer(128); + buf.append(num(rate.getAverageValue())).append(';'); + buf.append(num(rate.getExtremeAverageValue())).append(';'); + buf.append(pct(rate.getPercentageOfLifetimeValue())).append(';'); + if (rate.getLifetimeTotalEventTime() > 0) { + // bah saturation + buf.append("0;0;0;0;"); + } + long numPeriods = rate.getLifetimePeriods(); + buf.append(num(fudgeQuantity)).append(';'); + return buf.toString(); + } + /* report the same data for tx and rx, for enhanced anonymity */ private void includeAverageThroughput(Properties stats) { RateStat sendRate = _context.statManager().getRate("bw.sendRate"); From 5906fb71395adffd116804705b482005fe424e0a Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sat, 27 Jun 2009 01:11:53 +0000 Subject: [PATCH 46/58] * Console CSS: Move css file, make a classic theme to prep for CSS changes --- build.xml | 18 +- .../themes/console/classic/console.css | 0 .../themes/console/classic/default.css | 182 ++++++++++++++++++ .../themes/console/classic/i2ptunnel.css | 178 +++++++++++++++++ .../resources/themes/console/console.css | 112 +++++++++++ .../themes/console/defCon1/console.css | 112 +++++++++++ 6 files changed, 595 insertions(+), 7 deletions(-) rename apps/routerconsole/jsp/default.css => installer/resources/themes/console/classic/console.css (100%) create mode 100644 installer/resources/themes/console/classic/default.css create mode 100644 installer/resources/themes/console/classic/i2ptunnel.css create mode 100644 installer/resources/themes/console/console.css create mode 100644 installer/resources/themes/console/defCon1/console.css diff --git a/build.xml b/build.xml index b80f6fd12..da8cfc4c2 100644 --- a/build.xml +++ b/build.xml @@ -297,9 +297,6 @@ <copy todir="pkg-temp/docs/themes/" > <fileset dir="installer/resources/themes/" /> </copy> - <!-- CSS now in docs/, not in the .war --> - <copy file="apps/routerconsole/jsp/default.css" tofile="pkg-temp/docs/themes/console/console.css" /> - <copy file="apps/routerconsole/jsp/default.css" tofile="pkg-temp/docs/themes/console/defCon1/console.css" /> <mkdir dir="pkg-temp/eepsite" /> <mkdir dir="pkg-temp/eepsite/webapps" /> <mkdir dir="pkg-temp/eepsite/logs" /> @@ -319,6 +316,16 @@ <copy file="installer/lib/launch4j/lib/JGoodies.Looks.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-JGoodies-Looks.txt" /> <copy file="installer/lib/launch4j/lib/XStream.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-XStream.txt" /> </target> + <target name="prepthemeupdates"> + <!-- CSS now in docs/, not in the .war --> + <copy file="installer/resources/themes/console/console.css" todir="pkg-temp/docs/themes/console/" /> + <copy file="installer/resources/themes/console/defCon1/console.css" todir="pkg-temp/docs/themes/console/defCon1/" /> + <!-- make a "classic" theme --> + <copy todir="pkg-temp/docs/themes/console/classic/" > + <fileset dir="installer/resources/themes/console/classic/" /> + </copy> + </target> + <!-- this is no longer required, izpack 4.3.0 supports headless installs with java -jar i2pinstall.exe -console --> <target name="tarball" depends="preppkg"> <tar compression="bzip2" destfile="i2p.tar.bz2"> @@ -364,7 +371,7 @@ <copy file="history.txt" todir="pkg-temp/" /> <copy file="news.xml" todir="pkg-temp/docs/" /> </target> - <target name="prepupdateSmall" depends="buildSmall, prepupdateRouter"> + <target name="prepupdateSmall" depends="buildSmall, prepupdateRouter, prepthemeupdates"> <copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" /> <copy file="build/mstreaming.jar" todir="pkg-temp/lib/" /> <copy file="build/streaming.jar" todir="pkg-temp/lib/" /> @@ -374,9 +381,6 @@ <copy file="build/addressbook.war" todir="pkg-temp/webapps/" /> <!-- New readme_*.html files - For one release only --> <copy file="readme_zh.html" todir="pkg-temp/docs/" /> - <!-- CSS now in docs/, not in the .war --> - <copy file="apps/routerconsole/jsp/default.css" tofile="pkg-temp/docs/themes/console/console.css" /> - <copy file="apps/routerconsole/jsp/default.css" tofile="pkg-temp/docs/themes/console/defCon1/console.css" /> </target> <target name="prepupdateRouter" depends="buildrouter, deletepkg-temp"> <copy file="build/i2p.jar" todir="pkg-temp/lib/" /> diff --git a/apps/routerconsole/jsp/default.css b/installer/resources/themes/console/classic/console.css similarity index 100% rename from apps/routerconsole/jsp/default.css rename to installer/resources/themes/console/classic/console.css diff --git a/installer/resources/themes/console/classic/default.css b/installer/resources/themes/console/classic/default.css new file mode 100644 index 000000000..88aa40d7f --- /dev/null +++ b/installer/resources/themes/console/classic/default.css @@ -0,0 +1,182 @@ +body { + margin : 0px; + padding : 0px; + text-align : center; + font-family : Arial, Helvetica, sans-serif; + background-color : #ffffff; + color : #000000; + font-size : 100%; + + /* we've avoided Tantek Hacks so far, + ** but we can't avoid using the non-w3c method of + ** box rendering. (and therefore one of mozilla's + ** proprietry -moz properties (which hopefully they'll + ** drop soon). + */ + -moz-box-sizing : border-box; + box-sizing : border-box; +} + +div { + -moz-box-sizing : border-box; + box-sizing : border-box; +} + +h4, label { + margin : 0px; + padding : 2px; + float : left; + width : 150px; + height : 24px; + font-weight : bold; + text-align : right; + font-size : 1.0em; + -moz-box-sizing: border-box; + box-sizing : border-box; +} + +h4 { + font-size : 1.2em; + text-align : center; + width : 750px; +} + +a { + text-decoration : none; +} + +form { + margin : 0px; +} + +textarea, input, select, button, a { + font-family : Arial, Helvetica, sans-serif; + -moz-box-sizing : border-box; + box-sizing : border-box; + font-size : 1.0em; + float : left; +} + +button { + float : none; +} + +textarea { + border : 1px solid #ddddc0; +} + +br { + clear : left; +} + +div.statusNotRunning { + float : left; + width : 82px; + height : 24px; + color : #dd0000; +} +div.statusRunning { + float : left; + width : 82px; + height : 24px; + color : #00dd00; +} +div.statusStarting { + float : left; + width : 82px; + height : 24px; + color : #339933; +} + +hr { + display : none; +} + +.separator, .subdivider { + clear : both; + height : 1px; + margin : 3px 0px 3px 0px; + border-bottom : 1px solid #ddddc0; +} + +.subdivider { + border-bottom : 1px dashed #ddddc0; +} + +.freetext { + width : 150px; + height : 22px; + border : 1px solid #aaaac0; +} + +.control { + margin : 0 4px 0 0; + padding : 0 0 4px 0; + overflow : hidden; + height : 20px; + width : 60px; + font-weight : normal; + background-color : #dddddd; + color : black; + border : 1px outset #ddddc0; + text-align : center; + white-space : nowrap; +} + +.control:hover { + background-color : #ffffed; +} + +.control:active { + border : 2px inset; +} + +.panel { + width : 760px; + margin : 16px auto 16px auto; + overflow : hidden; + text-align : left; + font-size : 0.8em; + background-color : #ffffef; + border : 4px solid #ffffd0; +} + +.panel .footer { + float : right; + padding : 4px; +} + +.toolbox { + float : right; +} + +.rowItem { + width : 750px; + float : left; + margin : 0px; +} + +.comment { + font-style : italic; +} + +.text { + height : 24px; + width : 150px; + padding : 2px 0 0 2px; + float : left; + margin : 0; +} + +.accessKey { + text-decoration : underline; +} + +#globalOperationsPanel { + background-color : #ffefef; + border : 4px solid #ffd0d0; +} + +#globalOperationsPanel .control { + width : 100px; +} diff --git a/installer/resources/themes/console/classic/i2ptunnel.css b/installer/resources/themes/console/classic/i2ptunnel.css new file mode 100644 index 000000000..814cab892 --- /dev/null +++ b/installer/resources/themes/console/classic/i2ptunnel.css @@ -0,0 +1,178 @@ +/* I2P Tunnel Edit Page +*/ + +#tunnelEditPage input { + width : 458px; +} + +#tunnelEditPage select { + width : 308px; +} + +#tunnelEditPage option[selected] { + color: green; +} + +#tunnelEditPage #targetField, +#tunnelEditPage #accessField, +#tunnelEditPage #optionsField { + height : 48px; + width : 150px; +} +#tunnelEditPage #tunnelOptionsField { + height : 96px; + width : 150px; +} + +#tunnelEditPage #targetField label, +#tunnelEditPage #accessField label, +#tunnelEditPage #tunnelOptionsField label, +#tunnelEditPage #optionsField label{ + height : 48px; + width : 150px; +} + +#tunnelEditPage #reachField, +#tunnelEditPage #hostField, +#tunnelEditPage #depthField, +#tunnelEditPage #countField, +#tunnelEditPage #optionsHostField { + width : 304px; + margin-right: 4px; +} + +#tunnelEditPage #portField, +#tunnelEditPage #optionsPortField, +#tunnelEditPage #backupField, +#tunnelEditPage #varianceField { + width : 150px; + +} + +#tunnelEditPage #reachField label, +#tunnelEditPage #hostField label, +#tunnelEditPage #portField label, +#tunnelEditPage #optionsHostField label, +#tunnelEditPage #optionsPortField label, +#tunnelEditPage #depthField label, +#tunnelEditPage #countField label, +#tunnelEditPage #backupField label, +#tunnelEditPage #varianceField label { + text-align : left; + +} + +#tunnelEditPage #otherField label { + width : 300px; +} + +#tunnelEditPage #reachableByOther, +#tunnelEditPage #tunnelDepth, +#tunnelEditPage #tunnelQuantity, +#tunnelEditPage #targetHost, +#tunnelEditPage #clientHost { + width : 306px; +} + +#tunnelEditPage #port { + width : 80px; +} + +#tunnelEditPage #targetPort, +#tunnelEditPage #clientPort, +#tunnelEditPage #tunnelBackupQuantity, +#tunnelEditPage #tunnelVariance { + width : 150px; +} + +#tunnelEditPage #shared, +#tunnelEditPage #connectDelay, +#tunnelEditPage #startOnLoad { + width : 16px; +} + +#tunnelEditPage label { + width : 150px; + font-weight : bold; + text-align : right; + float : left; +} + +/* I2P Tunnel List Page +*/ + +#tunnelListPage .rowItem { + width : 150px; +} + +#tunnelListPage select { + width : 150px; +} + +#tunnelListPage textarea { + width : 750px; + height : 100px; + padding : 0 0 0 4px; +} + +#tunnelListPage .footer .control { + margin-left: 2px; +} + +#tunnelListPage .footer label { + text-align : right; + height : 24px; + width : 360px; + float : left; + +} + +/* Use Leary and Langridge content replacement methods (LIR) +** to embed accessibility information into the document. +** Should allow the lists to be rendered nicely by +** screen readers. (and lynx!) +*/ + +#tunnelListPage label { + height : 0; + width : 0; + overflow : hidden; +} + +#tunnelListPage .nameHeaderField label, +#tunnelListPage .portHeaderField label, +#tunnelListPage .typeHeaderField label, +#tunnelListPage .interfaceHeaderField label, +#tunnelListPage .targetHeaderField label, +#tunnelListPage .previewHeaderField label, +#tunnelListPage .statusHeaderField label { + text-align : left; + width : 150px; + height : 24px; + float : left; +} + +#tunnelListPage .targetField, +#tunnelListPage .targetField .text, +#tunnelListPage .targetHeaderField, +#tunnelListPage .targetHeaderField label { + width : 300px; +} + +#tunnelListPage .descriptionField, +#tunnelListPage .destinationField { + width : 750px; +} + +#tunnelListPage .descriptionField .text, +#tunnelListPage .destinationField input { + width : 450px; +} + +#tunnelListPage .descriptionField label, +#tunnelListPage .destinationField label { + text-align : right; + width : 150px; + height : 24px; + float : left; +} diff --git a/installer/resources/themes/console/console.css b/installer/resources/themes/console/console.css new file mode 100644 index 000000000..99077d1e0 --- /dev/null +++ b/installer/resources/themes/console/console.css @@ -0,0 +1,112 @@ +body { + font-family: Verdana, Tahoma, Helvetica, sans-serif; + margin: 1em 0em; + padding: 0em; + text-align: center; + background-color: white; + color: black; + font-size: 100%; +} + +.hide { + display: none; +} + +img { + border: none; +} + +pre { + width: 100%; + overflow-x: scroll; +} + +div.logo { + float: left; + width: 200px; + left: 1em; + top: 1em; + margin: 0em; + padding: .5em; + text-align: center; +} + +div.toolbar { + margin: 0em 0em 2em 0em; + font-weight: bold; +} + +div.routersummaryouter { + float: left; + width: 215px; + margin: 0; + padding: 0; + border: 0; + clear: left; /* fixes a bug in Opera */ + overflow: auto; +} + +div.routersummary { + background-color: #fafaff; + width: 195px; + color: inherit; + margin: 0em; + padding: 5px; + text-align: left; + border: medium solid #efefff; + font-size: 0.82em; +} + +div.warning { + margin: 0em 1em 1em 224px; + padding: .5em 1em; + background-color: #ffefef; + border: medium solid #ffafaf; + text-align: left; + color: inherit; +} + +div.main { + margin: 0em 1em 1em 224px; + padding: .5em 1em; + background-color: #ffffef; + border: medium solid #ffffd0; + text-align: left; + color: inherit; +} + +div.main textarea { + width: 100% !important; +} + +div.news { + margin: 0em 1em 1em 224px; + padding: .5em 1em; + background-color: #ffffc0; + border: medium solid #ffffa0; + text-align: left; + color: inherit; +} + +div.confignav { + padding: 1em; + background-color: #efefff; +} + +div.configure { + padding: 1em; + background-color: #ffffc0; +} + +div.messages { + padding: 1em; + background-color: #fafaff; +} + +div.messages span.error { + color: #d00000; +} + +div.messages span.notice { + font-style: italic; +} diff --git a/installer/resources/themes/console/defCon1/console.css b/installer/resources/themes/console/defCon1/console.css new file mode 100644 index 000000000..99077d1e0 --- /dev/null +++ b/installer/resources/themes/console/defCon1/console.css @@ -0,0 +1,112 @@ +body { + font-family: Verdana, Tahoma, Helvetica, sans-serif; + margin: 1em 0em; + padding: 0em; + text-align: center; + background-color: white; + color: black; + font-size: 100%; +} + +.hide { + display: none; +} + +img { + border: none; +} + +pre { + width: 100%; + overflow-x: scroll; +} + +div.logo { + float: left; + width: 200px; + left: 1em; + top: 1em; + margin: 0em; + padding: .5em; + text-align: center; +} + +div.toolbar { + margin: 0em 0em 2em 0em; + font-weight: bold; +} + +div.routersummaryouter { + float: left; + width: 215px; + margin: 0; + padding: 0; + border: 0; + clear: left; /* fixes a bug in Opera */ + overflow: auto; +} + +div.routersummary { + background-color: #fafaff; + width: 195px; + color: inherit; + margin: 0em; + padding: 5px; + text-align: left; + border: medium solid #efefff; + font-size: 0.82em; +} + +div.warning { + margin: 0em 1em 1em 224px; + padding: .5em 1em; + background-color: #ffefef; + border: medium solid #ffafaf; + text-align: left; + color: inherit; +} + +div.main { + margin: 0em 1em 1em 224px; + padding: .5em 1em; + background-color: #ffffef; + border: medium solid #ffffd0; + text-align: left; + color: inherit; +} + +div.main textarea { + width: 100% !important; +} + +div.news { + margin: 0em 1em 1em 224px; + padding: .5em 1em; + background-color: #ffffc0; + border: medium solid #ffffa0; + text-align: left; + color: inherit; +} + +div.confignav { + padding: 1em; + background-color: #efefff; +} + +div.configure { + padding: 1em; + background-color: #ffffc0; +} + +div.messages { + padding: 1em; + background-color: #fafaff; +} + +div.messages span.error { + color: #d00000; +} + +div.messages span.notice { + font-style: italic; +} From b23256dc4eecd2bd2f497534f641f0ea066b1a56 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sat, 27 Jun 2009 17:52:08 +0000 Subject: [PATCH 47/58] * HTTP Proxy: - Add simple web server for "proxy.i2p" to serve images and CSS for the error pages z --- .../i2p/i2ptunnel/I2PTunnelHTTPClient.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index cab438991..8dabdc0f6 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -553,6 +553,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix(requestId) + "Destination: " + destination); + // Serve local proxy files (images, css linked from error pages) + // Ignore all the headers + if (destination.equals("proxy.i2p")) { + serveLocalFile(out, method, targetRequest); + s.close(); + return; + } + Destination dest = I2PTunnel.destFromName(destination); if (dest == null) { //l.log("Could not resolve " + destination + "."); @@ -778,4 +786,70 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable return protocol.equalsIgnoreCase("http://"); } + + private final static byte[] ERR_404 = + ("HTTP/1.1 404 Not Found\r\n"+ + "Content-Type: text/plain\r\n"+ + "\r\n"+ + "HTTP Proxy local file not found") + .getBytes(); + + /** + * Very simple web server. + * + * Serve local files in the docs/ directory, for CSS and images in + * error pages, using the reserved address proxy.i2p + * (similar to p.p in privoxy). + * This solves the problems with including links to the router console, + * as assuming the router console is at 127.0.0.1 leads to broken + * links if it isn't. + * + * Ignore all request headers (If-Modified-Since, etc.) + * + * There is basic protection here - + * FileUtil.readFile() prevents traversal above the base directory - + * but inproxy/gateway ops would be wise to block proxy.i2p to prevent + * exposing the docs/ directory or perhaps other issues through + * uncaught vulnerabilities. + * + * @param targetRequest "proxy.i2p/foo.png HTTP/1.1" + */ + private static void serveLocalFile(OutputStream out, String method, String targetRequest) { + if (method.equals("GET") || method.equals("HEAD")) { + int space = targetRequest.indexOf(' '); + String filename = null; + try { + filename = targetRequest.substring(10, space); + } catch (IndexOutOfBoundsException ioobe) {} + if (filename == null || filename.length() <= 0) { + try { + out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes()); + out.flush(); + } catch (IOException ioe) {} + return; + } + File file = new File(_errorDir, filename); + if (file.exists() && !file.isDirectory()) { + String type; + if (filename.endsWith(".css")) + type = "text/css"; + else if (filename.endsWith(".png")) + type = "image/png"; + else if (filename.endsWith(".jpg")) + type = "image/jpeg"; + else type = "text/html"; + try { + out.write("HTTP/1.1 200 OK\r\nContent-Type: ".getBytes()); + out.write(type.getBytes()); + out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes()); + FileUtil.readFile(filename, _errorDir.getAbsolutePath(), out); + return; + } catch (IOException ioe) {} + } + } + try { + out.write(ERR_404); + out.flush(); + } catch (IOException ioe) {} + } } From 77ce768cb496e92b83ff7b4d3d105dfac3ee70aa Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 28 Jun 2009 12:50:33 +0000 Subject: [PATCH 48/58] * Console: Move favicon.ico and i2plogo.png out of the .war so that the HTTP proxy can use them directly; proxy error pages must be updated next --- apps/routerconsole/jsp/index.jsp | 2 +- apps/routerconsole/jsp/nav.jsp | 2 +- apps/routerconsole/jsp/viewtheme.jsp | 4 +++- build.xml | 7 +++++++ installer/resources/favicon.ico | Bin 1406 -> 0 bytes .../resources/themes/console/images}/favicon.ico | Bin .../resources/themes/console/images}/i2plogo.png | Bin 7 files changed, 12 insertions(+), 3 deletions(-) delete mode 100644 installer/resources/favicon.ico rename {apps/routerconsole/jsp => installer/resources/themes/console/images}/favicon.ico (100%) rename {apps/routerconsole/jsp => installer/resources/themes/console/images}/i2plogo.png (100%) diff --git a/apps/routerconsole/jsp/index.jsp b/apps/routerconsole/jsp/index.jsp index 87287b412..73ad4e15b 100644 --- a/apps/routerconsole/jsp/index.jsp +++ b/apps/routerconsole/jsp/index.jsp @@ -6,7 +6,7 @@ <title>I2P Router Console - home <%@include file="css.jsp" %> - + <% if (System.getProperty("router.consoleNonce") == null) { diff --git a/apps/routerconsole/jsp/nav.jsp b/apps/routerconsole/jsp/nav.jsp index ad0021487..43d8d336e 100644 --- a/apps/routerconsole/jsp/nav.jsp +++ b/apps/routerconsole/jsp/nav.jsp @@ -1,6 +1,6 @@ <%@page import="java.io.File" %>
<% if (new File("docs/toolbar.html").exists()) { %> diff --git a/apps/routerconsole/jsp/viewtheme.jsp b/apps/routerconsole/jsp/viewtheme.jsp index 05ccfecbf..95b1e91c0 100644 --- a/apps/routerconsole/jsp/viewtheme.jsp +++ b/apps/routerconsole/jsp/viewtheme.jsp @@ -14,7 +14,9 @@ if (uri.endsWith(".css")) { response.setContentType("image/gif"); } else if (uri.endsWith(".jpg")) { response.setContentType("image/jpeg"); +} else if (uri.endsWith(".ico")) { + response.setContentType("image/x-icon"); } - +response.setHeader("Cache-Control", "max-age=86400"); // cache for a day net.i2p.util.FileUtil.readFile(uri, "./docs", response.getOutputStream()); %> \ No newline at end of file diff --git a/build.xml b/build.xml index da8cfc4c2..525364929 100644 --- a/build.xml +++ b/build.xml @@ -320,6 +320,13 @@ + + + + + + + diff --git a/installer/resources/favicon.ico b/installer/resources/favicon.ico deleted file mode 100644 index fd09b1b4cc69cb3eaa0853bda10698e3105d261f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1406 zcmbtUO=weD6#ibD)U+uYpS4Eks?o+;$8>C=62%Uo(uIr4%%aOKsuXeIvJhb&Xwr@d z(?zRU)F04AabqbcVunFPM06O`MM+$gqOk^%<{=tml=^JuJNKm!8ph%dqvYVs5nQs*4>misZfG=|4d9eAGSVJ|uuShNu? zc42zP!HM!P@(T{~%Ql*d5u}PGsD(W4+;vb|E@5hE8kuJfhToRZ$e8`*A*7ZrqpR49 z;j)Fuf{h0c9XQJl`pN?sTS_4_>!6XjhL|T(?8dE|lE*^+wF8@Z6my5g@4n=4FhA$O z((%JEd)@kjPk3G@|3jV!ayX7Cn;XG;ZIB%91`0O;vqfXzBrwb7dtE)I_EBF;y{k5^ z@$4gKsgDC^(GC0E8<<~da0*CIZ9uwk5ff$x77el`3+X-^6Il}$?I#C=*jspuW^-fT=KjRDmD~CpB9?X9q1E^h@!^8qff2J=rpqtU!`T#&poic};3@f6 zRAMo1LmXblHyA&`_@ci-kN-HY9{p7V!Q>ab#8J*e1{TMjnI0_r58!lXO!t>m6x*{% z@Bno(bqo$B|+s~9Xw6}+OM7*R`N`=LvL8&!@LrUp& z+^JMs74H;1@oK?MN^KNe%K)o4wM8V!mhIaGeoy|bei3fiyxEkq#W0MYXfFN~>?Smf yI@K;XsMIcvw@RBj-#U$VDEZTq`cypy0i{}lT>7;VaGgiTSFNprz9I4Y5&9p!OB Date: Sun, 28 Jun 2009 12:55:10 +0000 Subject: [PATCH 49/58] * HTTP Proxy: - Take CSS out of the error pages; use internal server for CSS, image, and favicon --- .../resources/ahelper-conflict-header.ht | 63 ++++++------------ installer/resources/dnf-header.ht | 27 +------- installer/resources/dnfb-header.ht | 27 +------- installer/resources/dnfh-header.ht | 27 +------- installer/resources/dnfp-header.ht | 66 +++++++------------ 5 files changed, 52 insertions(+), 158 deletions(-) diff --git a/installer/resources/ahelper-conflict-header.ht b/installer/resources/ahelper-conflict-header.ht index 7f34d21cc..068558d6a 100644 --- a/installer/resources/ahelper-conflict-header.ht +++ b/installer/resources/ahelper-conflict-header.ht @@ -4,45 +4,24 @@ Cache-control: no-cache Connection: close Proxy-Connection: close - -Destination key conflict - - - - - -
-The addresshelper link you followed specifies a different destination key -than a host entry in your host database. -Someone could be trying to impersonate another eepsite, -or people have given two eepsites identical names. -

-You can resolve the conflict by considering which key you trust, -and either discarding the addresshelper link, -discarding the host entry from your host database, -or naming one of them differently. -

+ +Destination key conflict + + + + +

+
+The addresshelper link you followed specifies a different destination key +than a host entry in your host database. +Someone could be trying to impersonate another eepsite, +or people have given two eepsites identical names. +

+You can resolve the conflict by considering which key you trust, +and either discarding the addresshelper link, +discarding the host entry from your host database, +or naming one of them differently. +

diff --git a/installer/resources/dnf-header.ht b/installer/resources/dnf-header.ht index 2293d938c..8de2824bf 100644 --- a/installer/resources/dnf-header.ht +++ b/installer/resources/dnf-header.ht @@ -6,33 +6,12 @@ Proxy-Connection: close Eepsite not reachable - - + +

diff --git a/installer/resources/dnfb-header.ht b/installer/resources/dnfb-header.ht index 51f9c63ad..890a1910f 100644 --- a/installer/resources/dnfb-header.ht +++ b/installer/resources/dnfb-header.ht @@ -6,33 +6,12 @@ Proxy-Connection: close Invalid eepsite destination - - + +
diff --git a/installer/resources/dnfh-header.ht b/installer/resources/dnfh-header.ht index eea94252e..307621aaa 100644 --- a/installer/resources/dnfh-header.ht +++ b/installer/resources/dnfh-header.ht @@ -6,33 +6,12 @@ Proxy-Connection: close Eepsite unknown - - + +
diff --git a/installer/resources/dnfp-header.ht b/installer/resources/dnfp-header.ht index 40358c3eb..b579e64b9 100644 --- a/installer/resources/dnfp-header.ht +++ b/installer/resources/dnfp-header.ht @@ -4,47 +4,25 @@ Cache-control: no-cache Connection: close Proxy-Connection: close - -Outproxy Not Found - - - - - - -
-The WWW Outproxy was not found. -It is offline, there is network congestion, -or your router is not yet well-integrated with peers. -You may want to -retry -as this will randomly reselect an outproxy from the pool you have defined -here -(if you have more than one configured). -If you continue to have trouble you may want to edit your outproxy list -here. -

Could not find the following destination:

+ +Outproxy Not Found + + + + + +
+The WWW Outproxy was not found. +It is offline, there is network congestion, +or your router is not yet well-integrated with peers. +You may want to +retry +as this will randomly reselect an outproxy from the pool you have defined +here +(if you have more than one configured). +If you continue to have trouble you may want to edit your outproxy list +here. +

Could not find the following destination:

From 256c5356fb84a56cc5857a2bd8afb042c51aa0c0 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 28 Jun 2009 17:40:17 +0000 Subject: [PATCH 50/58] Add router log location to logs.jsp --- .../java/src/net/i2p/router/web/LogsHelper.java | 3 ++- core/java/src/net/i2p/util/LogManager.java | 4 ++++ core/java/src/net/i2p/util/LogWriter.java | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java index 9aee053f5..e7fb539ec 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java @@ -9,7 +9,8 @@ public class LogsHelper extends HelperBase { public LogsHelper() {} public String getLogs() { - return formatMessages(_context.logManager().getBuffer().getMostRecentMessages()); + String str = formatMessages(_context.logManager().getBuffer().getMostRecentMessages()); + return "Location: " + _context.logManager().currentFile() + "
" + str; } public String getCriticalLogs() { diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index c07445d9b..73178b0d6 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -202,6 +202,10 @@ public class LogManager { loadConfig(); } + public String currentFile() { + return _writer.currentFile(); + } + /** * Used by Log to add records to the queue * diff --git a/core/java/src/net/i2p/util/LogWriter.java b/core/java/src/net/i2p/util/LogWriter.java index c9f2cb756..b6302c3c4 100644 --- a/core/java/src/net/i2p/util/LogWriter.java +++ b/core/java/src/net/i2p/util/LogWriter.java @@ -93,7 +93,10 @@ class LogWriter implements Runnable { } } - + public String currentFile() { + return _currentFile != null ? _currentFile.getAbsolutePath() : "uninitialized"; + } + private void rereadConfig() { long now = Clock.getInstance().now(); if (now - _lastReadConfig > CONFIG_READ_ITERVAL) { From 9e1181900be79b925c0d71ea2cf1f557bb396d32 Mon Sep 17 00:00:00 2001 From: complication Date: Mon, 29 Jun 2009 01:07:51 +0000 Subject: [PATCH 51/58] * Update versions, package release * Remove the last reference to my eepsite as a "news.xml" source, and likewise stop my public key from being included among valid release signing keys. --- .../router/configuration/UpdateHelper.java | 3 +- core/java/src/net/i2p/CoreVersion.java | 2 +- .../src/net/i2p/crypto/TrustedUpdate.java | 1 - history.txt | 8 ++++ initialNews.xml | 4 +- installer/install.xml | 2 +- news.xml | 45 ++++++++++++------- .../src/net/i2p/router/RouterVersion.java | 2 +- 8 files changed, 43 insertions(+), 24 deletions(-) diff --git a/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java index 86db6f708..6e28db108 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java +++ b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java @@ -9,7 +9,7 @@ import net.i2p.desktopgui.router.RouterHelper; public class UpdateHelper { public static final String PROP_NEWS_URL = "router.newsURL"; - public static final String DEFAULT_NEWS_URL = "http://complication.i2p/news.xml"; + public static final String DEFAULT_NEWS_URL = "http://echelon.i2p/i2p/news.xml"; public static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency"; public static final String DEFAULT_REFRESH_FREQUENCY = 24*60*60*1000 + ""; @@ -31,7 +31,6 @@ public class UpdateHelper { public static final String DEFAULT_UPDATE_URL = "http://echelon.i2p/i2p/i2pupdate.sud\r\n" + "http://stats.i2p/i2p/i2pupdate.sud\r\n" + - "http://complication.i2p/i2p/i2pupdate.sud\r\n" + "http://www.i2p2.i2p/_static/i2pupdate.sud\r\n" + "http://update.postman.i2p/i2pupdate.sud" ; diff --git a/core/java/src/net/i2p/CoreVersion.java b/core/java/src/net/i2p/CoreVersion.java index 972b87966..fecc6c9a8 100644 --- a/core/java/src/net/i2p/CoreVersion.java +++ b/core/java/src/net/i2p/CoreVersion.java @@ -15,7 +15,7 @@ package net.i2p; */ public class CoreVersion { public final static String ID = "$Revision: 1.72 $ $Date: 2008-08-24 12:00:00 $"; - public final static String VERSION = "0.7.4"; + public final static String VERSION = "0.7.5"; public static void main(String args[]) { System.out.println("I2P Core version: " + VERSION); diff --git a/core/java/src/net/i2p/crypto/TrustedUpdate.java b/core/java/src/net/i2p/crypto/TrustedUpdate.java index 06e37544b..33a7e8cb7 100644 --- a/core/java/src/net/i2p/crypto/TrustedUpdate.java +++ b/core/java/src/net/i2p/crypto/TrustedUpdate.java @@ -141,7 +141,6 @@ D8usM7Dxp5yrDrCYZ5AIijc= } else { _trustedKeys.add(DEFAULT_TRUSTED_KEY); _trustedKeys.add(DEFAULT_TRUSTED_KEY2); - _trustedKeys.add(DEFAULT_TRUSTED_KEY3); } if (_log.shouldLog(Log.DEBUG)) _log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys."); diff --git a/history.txt b/history.txt index 33381eb02..dfd639beb 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,11 @@ +* 2009-06-29 0.7.5 released + +2009-06-29 Complication + * Update versions, package release + * Remove the last reference to my eepsite as a "news.xml" source, + and likewise stop my public key from being included + among valid release signing keys. + 2009-06-25 sponge * Summary frame layout change so it makes sense. diff --git a/initialNews.xml b/initialNews.xml index 0a2c5b12b..9a1d4bb78 100644 --- a/initialNews.xml +++ b/initialNews.xml @@ -1,5 +1,5 @@ - - +

Congratulations on getting I2P installed!

    diff --git a/installer/install.xml b/installer/install.xml index d51bd7f2a..b6aa5528d 100644 --- a/installer/install.xml +++ b/installer/install.xml @@ -4,7 +4,7 @@ i2p - 0.7.4 + 0.7.5 diff --git a/news.xml b/news.xml index e03588a6e..729462ac6 100644 --- a/news.xml +++ b/news.xml @@ -1,5 +1,5 @@ - - +

    • -2009-06-12: 0.7.4 Released +2009-06-29: 0.7.5 Released

    -I2P version 0.7.4 introduces notable new features -like GeoIP capability and UPnP support. While the former -can become a basis for geographically aware tunnel-building, -the latter should immediately enable more routers to accept -inbound TCP connections, helping distribute workload more evenly. +I2P version 0.7.5 foremost addresses network stability issues +by adjusting how exploratory tunnels are built if a router nears +its connection limit, and also by distinguishing between +connected peers and other non-failing peers while picking them +for inclusion in tunnels.

    -Inbound NTCP is now enabled automaticaly if the router -does not appear firewalled, and default bandwidth limits -for new installations are increased. +This version also delivers fixes to installer behaviour on Windows, +to the Router Console, NTCP transport and UPnP mechanism.

    -In addition, multiple bugfixes and updates are included, -addressing issues with the NTCP transport, BOB protocol, -connection limiting, behaviour of new I2PTunnel options -and the SusiDNS user interface. Improvements to the Router Console -are likewise included. Updating is recommended. +Additionally, version 0.7.5 changes the "news.xml" file location +where I2P routers check whether a new version is available. +Instead of the old URL (http://complication.i2p/news.xml) +a new location (http://echelon.i2p./news.xml) will be used. +If your router uses default updater settings, it will start using +the new URL automatically. If not, you will need to visit +configupdate.jsp, and change it manually. +For a while after this update, the old news.xml location will also +redirect all HTTP requests to the new one. +

    +Finally, since Complication is withdrawing from the I2P project, +to make some far-reaching changes in his life, this update removes +Complication's public key from the list of release signing keys. +You should expect the next release to be signed by zzz instead. +If you have non-default updater settings, you may want to remove +Complication's public key manually, by visiting configupdate.jsp, +and removing the key line starting with the characters "JHFA". +If you wish to verify whether this information is correct, +please check the GPG-signed release announcement.

    diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index b54d1de4b..8aa072fcf 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 5; + public final static long BUILD = 0; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; From 8ca794dc571cf52981f9a1533e14c7b600f313a8 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 29 Jun 2009 03:21:12 +0000 Subject: [PATCH 52/58] history for prop from test2 branch --- history.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/history.txt b/history.txt index dfd639beb..4e0957f47 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,12 @@ +2009-06-29 zzz + * Console: Convert table headers to to prep for CSS changes + * Console CSS: Move css file, make a classic theme to prep for CSS changes + * Console: Move favicon.ico and i2plogo.png out of the .war + so that the HTTP proxy can use them directly; + proxy error pages must be updated next + * NetDb stats: Normalize tunnel build stats for increased anonymity, + effective in 0.7.6 + * 2009-06-29 0.7.5 released 2009-06-29 Complication From 85adfc40fb9f8356565e5a60c5feeddb30218c5f Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 29 Jun 2009 04:20:21 +0000 Subject: [PATCH 53/58] build fixup, history for prop from test branch --- build.xml | 2 +- history.txt | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/build.xml b/build.xml index ddc27baad..162064b05 100644 --- a/build.xml +++ b/build.xml @@ -305,7 +305,7 @@ - + diff --git a/history.txt b/history.txt index 4e0957f47..51e030805 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,126 @@ +2009-06-29 zzz + * Big directory rework: + Eliminate all uses of the current working directory, and + set up multiple directories specified by absolute paths for various uses. + + Add a WorkingDir class to create a user config directory and + migrate certain files to it for new installs. + The directory will be $HOME/.i2p on linux and %APPDATA%\I2P on Windows, + or as specified in the system property -Di2p.dir.config=/path/to/i2pdir + All files except for the base install and temp files will be + in the config directory by default. + Temp files will be in a i2p-xxxxx subdirectory of the system temp directory + specified by the system property java.io.tmpdir. + + Convert all file opens in the code to be relative to a specific directory, + as specified in the context. Code and applications should never open + files relative to the current working directory (e.g. new File("foo")). + All files should be accessed in the appropriate context directory, + e.g. new File(_context.getAppDir(), "foo"). + + The router.config file location may be specified as a system property on the + java command line with -Drouter.configLocation=/path/to/router.config + All directories may be specified as properties in the router.config file. + + There will be no migration from an existing installation + unless the system property -Di2p.dir.migrate=true is set. + If there is no migration, it will continue to use $I2P for all files, + except for temporary and PID files. + + The following linux scripts are now customized with the install path at, + installation, and may be moved to /usr/local/bin and run from any + working directory: + eepget, i2prouter, runplain.sh + + For new installs, the i2p base directory ($I2P) may be read-only + if updates are disabled. The only time i2p should write to the base directory + is to unzip the update file. Updates are downloaded to the config dir. If, upon + restart, the base dir is not writable, it will log a message and continue. + + Additional information, copied from I2PAppContext: + + * Directories. These are all set at instantiation and will not be changed by + * subsequent property changes. + * All properties, if set, should be absolute paths. + * + * Name Property Method Files + * ----- -------- ----- ----- + * Base i2p.dir.base getBaseDir() lib/, webapps/, docs/, geoip/, licenses/, ... + * Temp i2p.dir.temp getTempDir() Temporary files + * PID i2p.dir.pid getPIDDir() router.ping + * Config i2p.dir.config getConfigDir() *.config, hosts.txt, addressbook/, ... + * + * (the following all default to the same as Config) + * + * Router i2p.dir.router getRouterDir() netDb/, peerProfiles/, router.*, keyBackup/, ... + * Log i2p.dir.log getLogDir() logs/ + * App i2p.dir.app getAppDir() eepsite/, ... + * + * Note that the router can't control where the wrapper actually puts its files. + + All these will be set appropriately in a Router Context. + In an I2P App Context, all except Temp and PID will be the current working directory. + + Related changes: + i2prouter: + - Don't cd to script location, no longer required + jbigi, cpuid: + - Extract files from jar to temp dir, load from that dir, then + copy to the base dir if we have permissions (and failing silently + if we don't), so we have optimized libs and no complaints + when we have a read-only base dir. + logs.jsp: + - Get wrapper log location from a property too + - Display log file locations + RouterLaunch: + - If no wrapper, put wrapper.log in system temp dir + unless specified with -Dwrapper.logfile=/path/to/wrapper.log + or it already exists in CWD (for backward compatibility) + - Append rather than replace wrapper.log + - Pass wrapper log location to router as a property, so that logs.jsp can find it + runplain.sh: + - Add path substitution to runplain.sh on install + - Pass I2P base dir to the router as a property + Systray: + - Fix NPE if no config file + wrapper.config: + - Put wrapper.log in system temp dir for new installs + - Pass I2P base dir to the router as a property + +2009-06-29 zzz + * HTTP Proxy: + - Add simple web server for "proxy.i2p" to serve + images and CSS for the error pages + - Take CSS out of the error pages; use internal server + for CSS, image, and favicon + * i2psnark build: + - Move FetchAndAdd to static inner class + - Fix standalone build to include i2psnark.jar since classes + aren't in the .war anymore + - Have standalone jetty use I2PAppContext temp directory + - Replace launch-i2psnark.jar with launch-i2psnark script, + since RunStandalone is in i2p.jar + - Clean up jetty-i2psnark.xml, turn off jetty logging + - Remove standalone build from the pkg target in the main build.xml + * Jbigi, CPUID: + - Reduce memory demand on startup from 4MB to 4KB each + * NetDb: Fix an NPE on early shutdown + * Reseeding / NetDb: + - Move reseeding from the routerconsole app to + the router, so that we can bootstrap an embedded router lacking a routerconsole + (iMule or android for example), without additional modifications. + This allows better integration between the reseeding function + and the netDb. + - Call reseed from PersistentDataStore, not from the + routerconsole init, and start seeding as soon as the netdb has read + the netDb/ directory, not when the console starts. + - Wake up the netdb reader as soon as reseeding is done, + rather than waiting up to 60s. + - Don't display the reseed button on the console until the + netdb initialization is done. + * RouterConsoleRunner: + - Catch a class not found error better + 2009-06-29 zzz * Console: Convert table headers to to prep for CSS changes * Console CSS: Move css file, make a classic theme to prep for CSS changes From eb324d7652e5149a19368c1dbe9c82c5dae821a5 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 29 Jun 2009 04:50:16 +0000 Subject: [PATCH 54/58] remove trailing newline (again) --- apps/routerconsole/jsp/viewtheme.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/routerconsole/jsp/viewtheme.jsp b/apps/routerconsole/jsp/viewtheme.jsp index 2a0085e00..1f7976d2b 100644 --- a/apps/routerconsole/jsp/viewtheme.jsp +++ b/apps/routerconsole/jsp/viewtheme.jsp @@ -21,4 +21,4 @@ response.setHeader("Cache-Control", "max-age=86400"); // cache for a day String base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath() + java.io.File.separatorChar + "docs"; net.i2p.util.FileUtil.readFile(uri, base, response.getOutputStream()); -%> +%> \ No newline at end of file From a9345953f30f99af84b68e74cef410f46171b247 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 29 Jun 2009 13:29:35 +0000 Subject: [PATCH 55/58] remove complication as update source --- .../java/src/net/i2p/router/web/ConfigUpdateHandler.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java index 9366591fc..db20396ca 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java @@ -33,11 +33,9 @@ public class ConfigUpdateHandler extends FormHandler { public static final String DEFAULT_PROXY_PORT = "4444"; public static final String PROP_UPDATE_URL = "router.updateURL"; -// public static final String DEFAULT_UPDATE_URL = "http://dev.i2p.net/i2p/i2pupdate.sud"; public static final String DEFAULT_UPDATE_URL = "http://echelon.i2p/i2p/i2pupdate.sud\r\n" + "http://stats.i2p/i2p/i2pupdate.sud\r\n" + - "http://complication.i2p/i2p/i2pupdate.sud\r\n" + "http://www.i2p2.i2p/_static/i2pupdate.sud\r\n" + "http://update.postman.i2p/i2pupdate.sud" ; From c7541f819a01fd374b8462e18e4ed18b26aa0f86 Mon Sep 17 00:00:00 2001 From: sponge Date: Tue, 30 Jun 2009 04:44:13 +0000 Subject: [PATCH 56/58] 2009-06-30 sponge * General cleanup on streaming and ministreaming. This fixes some compile warnings, and prepares for a larger fix. There is no code-flow changes, just lint. One warning remains as I am unsure exactly how to solve the problem yet. --- apps/BOB/nbproject/private/private.xml | 3 ++ .../streaming/I2PSocketOptionsImpl.java | 2 +- .../net/i2p/client/streaming/Connection.java | 21 +++++----- .../client/streaming/ConnectionHandler.java | 12 +++--- .../client/streaming/ConnectionManager.java | 38 ++++++++++--------- .../client/streaming/ConnectionOptions.java | 7 ++-- .../i2p/client/streaming/MessageHandler.java | 2 +- .../client/streaming/MessageInputStream.java | 2 +- .../client/streaming/MessageOutputStream.java | 3 +- .../i2p/client/streaming/PacketHandler.java | 1 - .../net/i2p/client/streaming/PacketQueue.java | 2 +- .../client/streaming/SchedulerChooser.java | 2 +- .../i2p/client/streaming/TaskScheduler.java | 2 +- history.txt | 6 +++ .../src/net/i2p/router/RouterVersion.java | 2 +- 15 files changed, 59 insertions(+), 46 deletions(-) diff --git a/apps/BOB/nbproject/private/private.xml b/apps/BOB/nbproject/private/private.xml index c1f155a78..2482568bf 100644 --- a/apps/BOB/nbproject/private/private.xml +++ b/apps/BOB/nbproject/private/private.xml @@ -1,4 +1,7 @@ + + file:/root/NetBeansProjects/i2p.i2p/apps/BOB/src/net/i2p/BOB/MUXlisten.java + diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java index eb58b6871..334c86af0 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java @@ -6,7 +6,7 @@ import java.util.Properties; * Define the configuration for streaming and verifying data on the socket. * */ -class I2PSocketOptionsImpl implements I2PSocketOptions { +public class I2PSocketOptionsImpl implements I2PSocketOptions { private long _connectTimeout; private long _readTimeout; private long _writeTimeout; diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java index f73632683..e6d99c473 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java @@ -49,7 +49,7 @@ public class Connection { private boolean _isInbound; private boolean _updatedShareOpts; /** Packet ID (Long) to PacketLocal for sent but unacked packets */ - private Map _outboundPackets; + private final Map _outboundPackets; private PacketQueue _outboundQueue; private ConnectionPacketHandler _handler; private ConnectionOptions _options; @@ -66,7 +66,7 @@ public class Connection { private long _lastCongestionHighestUnacked; private boolean _ackSinceCongestion; /** Notify this on connection (or connection failure) */ - private Object _connectLock; + private final Object _connectLock; /** how many messages have been resent and not yet ACKed? */ private int _activeResends; private ConEvent _connectionEvent; @@ -90,21 +90,22 @@ public class Connection { } public Connection(I2PAppContext ctx, ConnectionManager manager, SchedulerChooser chooser, PacketQueue queue, ConnectionPacketHandler handler, ConnectionOptions opts) { _context = ctx; - _log = ctx.logManager().getLog(Connection.class); - _receiver = new ConnectionDataReceiver(ctx, this); - _inputStream = new MessageInputStream(ctx); - _outputStream = new MessageOutputStream(ctx, _receiver, (opts == null ? Packet.MAX_PAYLOAD_SIZE : opts.getMaxMessageSize())); + _connectionManager = manager; _chooser = chooser; - _outboundPackets = new TreeMap(); _outboundQueue = queue; _handler = handler; + _log = _context.logManager().getLog(Connection.class); + _receiver = new ConnectionDataReceiver(_context, this); + _inputStream = new MessageInputStream(_context); + _outputStream = new MessageOutputStream(_context, _receiver, (opts == null ? Packet.MAX_PAYLOAD_SIZE : opts.getMaxMessageSize())); + _outboundPackets = new TreeMap(); _options = (opts != null ? opts : new ConnectionOptions()); _outputStream.setWriteTimeout((int)_options.getWriteTimeout()); _inputStream.setReadTimeout((int)_options.getReadTimeout()); _lastSendId = -1; _nextSendTime = -1; _ackedPackets = 0; - _createdOn = ctx.clock().now(); + _createdOn = _context.clock().now(); _closeSentOn = -1; _closeReceivedOn = -1; _unackedPacketsReceived = 0; @@ -113,7 +114,6 @@ public class Connection { _lastCongestionSeenAt = MAX_WINDOW_SIZE*2; // lets allow it to grow _lastCongestionTime = -1; _lastCongestionHighestUnacked = -1; - _connectionManager = manager; _resetReceived = false; _connected = true; _disconnectScheduledOn = -1; @@ -126,7 +126,7 @@ public class Connection { _isInbound = false; _updatedShareOpts = false; _connectionEvent = new ConEvent(); - _hardDisconnected = false; + _hardDisconnected = false; _randomWait = _context.random().nextInt(10*1000); // just do this once to reduce usage _context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); @@ -1018,6 +1018,7 @@ public class Connection { // _log.debug("firing event on " + _connection, _addedBy); eventOccurred(); } + @Override public String toString() { return "event on " + Connection.this.toString(); } } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java index d989a2367..382c984d9 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java @@ -16,7 +16,7 @@ import net.i2p.util.SimpleTimer; * * @author zzz modded to use concurrent and bound queue size */ -class ConnectionHandler { +public class ConnectionHandler { private I2PAppContext _context; private Log _log; private ConnectionManager _manager; @@ -109,7 +109,7 @@ class ConnectionHandler { // fail all the ones we had queued up while(true) { Packet packet = _synQueue.poll(); // fails immediately if empty - if (packet == null || packet.getOptionalDelay() == PoisonPacket.MAX_DELAY_REQUEST) + if (packet == null || packet.getOptionalDelay() == PoisonPacket.POISON_MAX_DELAY_REQUEST) break; sendReset(packet); } @@ -142,7 +142,7 @@ class ConnectionHandler { } if (syn != null) { - if (syn.getOptionalDelay() == PoisonPacket.MAX_DELAY_REQUEST) + if (syn.getOptionalDelay() == PoisonPacket.POISON_MAX_DELAY_REQUEST) return null; // deal with forged / invalid syn packets @@ -226,14 +226,14 @@ class ConnectionHandler { /** * Simple end-of-queue marker. - * The standard class limits the delay to MAX_DELAY_REQUEST so + * The standard class limits the delay to POISON_MAX_DELAY_REQUEST so * an evil user can't use this to shut us down */ private static class PoisonPacket extends Packet { - public static final int MAX_DELAY_REQUEST = Packet.MAX_DELAY_REQUEST + 1; + public static final int POISON_MAX_DELAY_REQUEST = Packet.MAX_DELAY_REQUEST + 1; public PoisonPacket() { - setOptionalDelay(MAX_DELAY_REQUEST); + setOptionalDelay(POISON_MAX_DELAY_REQUEST); } } } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java index 7826ba2a8..b978ff3be 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java @@ -34,32 +34,32 @@ public class ConnectionManager { /** Inbound stream ID (Long) to Connection map */ private Map _connectionByInboundId; /** Ping ID (Long) to PingRequest */ - private Map _pendingPings; + private final Map _pendingPings; private boolean _allowIncoming; private int _maxConcurrentStreams; private ConnectionOptions _defaultOptions; private volatile int _numWaiting; - private Object _connectionLock; + private final Object _connectionLock; private long SoTimeout; public ConnectionManager(I2PAppContext context, I2PSession session, int maxConcurrent, ConnectionOptions defaultOptions) { _context = context; - _log = context.logManager().getLog(ConnectionManager.class); + _session = session; + _maxConcurrentStreams = maxConcurrent; + _defaultOptions = defaultOptions; + _log = _context.logManager().getLog(ConnectionManager.class); _connectionByInboundId = new HashMap(32); _pendingPings = new HashMap(4); _connectionLock = new Object(); - _messageHandler = new MessageHandler(context, this); - _packetHandler = new PacketHandler(context, this); - _connectionHandler = new ConnectionHandler(context, this); - _schedulerChooser = new SchedulerChooser(context); - _conPacketHandler = new ConnectionPacketHandler(context); - _tcbShare = new TCBShare(context); - _session = session; - session.setSessionListener(_messageHandler); - _outboundQueue = new PacketQueue(context, session, this); + _messageHandler = new MessageHandler(_context, this); + _packetHandler = new PacketHandler(_context, this); + _connectionHandler = new ConnectionHandler(_context, this); + _schedulerChooser = new SchedulerChooser(_context); + _conPacketHandler = new ConnectionPacketHandler(_context); + _tcbShare = new TCBShare(_context); + _session.setSessionListener(_messageHandler); + _outboundQueue = new PacketQueue(_context, _session, this); _allowIncoming = false; - _maxConcurrentStreams = maxConcurrent; - _defaultOptions = defaultOptions; _numWaiting = 0; /** Socket timeout for accept() */ SoTimeout = -1; @@ -277,11 +277,13 @@ public class ConnectionManager { public MessageHandler getMessageHandler() { return _messageHandler; } public PacketHandler getPacketHandler() { return _packetHandler; } - public ConnectionHandler getConnectionHandler() { return _connectionHandler; } public I2PSession getSession() { return _session; } - public PacketQueue getPacketQueue() { return _outboundQueue; } public void updateOptsFromShare(Connection con) { _tcbShare.updateOptsFromShare(con); } public void updateShareOpts(Connection con) { _tcbShare.updateShareOpts(con); } + // Both of these methods are + // exporting non-public type through public API, this is a potential bug. + public ConnectionHandler getConnectionHandler() { return _connectionHandler; } + public PacketQueue getPacketQueue() { return _outboundQueue; } /** * Something b0rked hard, so kill all of our connections without mercy. @@ -345,13 +347,13 @@ public class ConnectionManager { return new HashSet(_connectionByInboundId.values()); } } - public boolean ping(Destination peer, long timeoutMs) { return ping(peer, timeoutMs, true); } public boolean ping(Destination peer, long timeoutMs, boolean blocking) { return ping(peer, timeoutMs, blocking, null, null, null); } + public boolean ping(Destination peer, long timeoutMs, boolean blocking, SessionKey keyToUse, Set tagsToSend, PingNotifier notifier) { Long id = new Long(_context.random().nextLong(Packet.MAX_STREAM_ID-1)+1); PacketLocal packet = new PacketLocal(_context, peer); @@ -390,7 +392,7 @@ public class ConnectionManager { return ok; } - interface PingNotifier { + public interface PingNotifier { public void pingComplete(boolean ok); } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java index 4363e3f49..740fdcf82 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java @@ -15,7 +15,6 @@ public class ConnectionOptions extends I2PSocketOptionsImpl { private int _rtt; private int _rttDev; private int _rto; - private int _trend[]; private int _resendDelay; private int _sendAckDelay; private int _maxMessageSize; @@ -58,7 +57,10 @@ public class ConnectionOptions extends I2PSocketOptionsImpl { static final int DEFAULT_MAX_SENDS = 8; public static final int DEFAULT_INITIAL_RTT = 8*1000; static final int MIN_WINDOW_SIZE = 1; - + // Syncronization fix, but doing it this way causes NPE... + // private final int _trend[] = new int[TREND_COUNT]; + private int _trend[]; + /** * OK, here is the calculation on the message size to fit in a single * tunnel message without fragmentation. @@ -203,7 +205,6 @@ public class ConnectionOptions extends I2PSocketOptionsImpl { protected void init(Properties opts) { super.init(opts); _trend = new int[TREND_COUNT]; - setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE)); setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1)); setProfile(getInt(opts, PROP_PROFILE, PROFILE_BULK)); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java index 1ff65248d..98165cf7d 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java @@ -18,7 +18,7 @@ public class MessageHandler implements I2PSessionListener { private ConnectionManager _manager; private I2PAppContext _context; private Log _log; - private List _listeners; + private final List _listeners; public MessageHandler(I2PAppContext ctx, ConnectionManager mgr) { _manager = mgr; diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageInputStream.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageInputStream.java index 77a5014cf..7b87e5583 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/MessageInputStream.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageInputStream.java @@ -55,7 +55,7 @@ public class MessageInputStream extends InputStream { private byte[] _oneByte = new byte[1]; - private Object _dataLock; + private final Object _dataLock; public MessageInputStream(I2PAppContext ctx) { _context = ctx; diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java index 4a810d565..ab7c9374a 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java @@ -20,7 +20,7 @@ public class MessageOutputStream extends OutputStream { private Log _log; private byte _buf[]; private int _valid; - private Object _dataLock; + private final Object _dataLock; private DataReceiver _dataReceiver; private IOException _streamError; private boolean _closed; @@ -319,6 +319,7 @@ public class MessageOutputStream extends OutputStream { throwAnyError(); } + @Override public void close() throws IOException { if (_closed) { synchronized (_dataLock) { _dataLock.notifyAll(); } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java index 1d26d7b8c..ff43293a0 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java @@ -9,7 +9,6 @@ import net.i2p.I2PAppContext; import net.i2p.I2PException; import net.i2p.data.DataHelper; import net.i2p.util.Log; -import net.i2p.util.SimpleTimer; /** * receive a packet and dispatch it correctly to the connection specified, diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java index e91cbdb7d..db4adb27c 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java @@ -18,7 +18,7 @@ import net.i2p.util.Log; * mode=bestEffort doesnt block in the SDK. * */ -class PacketQueue { +public class PacketQueue { private I2PAppContext _context; private Log _log; private I2PSession _session; diff --git a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java index a2aacf82e..be62f1766 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java @@ -10,7 +10,7 @@ import net.i2p.util.Log; * Examine a connection's state and pick the right scheduler for it. * */ -class SchedulerChooser { +public class SchedulerChooser { private I2PAppContext _context; private Log _log; private TaskScheduler _nullScheduler; diff --git a/apps/streaming/java/src/net/i2p/client/streaming/TaskScheduler.java b/apps/streaming/java/src/net/i2p/client/streaming/TaskScheduler.java index 8657a2053..c998c8425 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/TaskScheduler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/TaskScheduler.java @@ -5,7 +5,7 @@ package net.i2p.client.streaming; * selected based upon its current state. * */ -interface TaskScheduler { +public interface TaskScheduler { /** * An event has occurred (timeout, message sent, or message received), * so schedule what to do next based on our current state. diff --git a/history.txt b/history.txt index dfd639beb..dc745e8e9 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,9 @@ +2009-06-30 sponge + * General cleanup on streaming and ministreaming. + This fixes some compile warnings, and prepares for a larger fix. + There is no code-flow changes, just lint. One warning remains as I am + unsure exactly how to solve the problem yet. + * 2009-06-29 0.7.5 released 2009-06-29 Complication diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 8aa072fcf..b74a3a021 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 0; + public final static long BUILD = 1; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; From 055cd99ddeab64d0044a0f5c3f5f87c5acc874c0 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 30 Jun 2009 13:14:31 +0000 Subject: [PATCH 57/58] -2 --- router/java/src/net/i2p/router/RouterVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index b74a3a021..bd644cee5 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ 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 = 2; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; From 72071566e7883157709c0ae14fbde556c9712af4 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 30 Jun 2009 17:56:51 +0000 Subject: [PATCH 58/58] javadoc fix --- core/java/src/net/i2p/util/WorkingDir.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/java/src/net/i2p/util/WorkingDir.java b/core/java/src/net/i2p/util/WorkingDir.java index 227a0c3be..a003d2b29 100644 --- a/core/java/src/net/i2p/util/WorkingDir.java +++ b/core/java/src/net/i2p/util/WorkingDir.java @@ -239,7 +239,7 @@ public class WorkingDir { * Recursive copy a file or dir to a dir * * @param src file or directory, need not exist - * @param target the directory to copy to, will be created if it doesn't exist + * @param targetDir the directory to copy to, will be created if it doesn't exist * @return true for success OR if src does not exist */ public static final boolean copy(File src, File targetDir) { @@ -277,8 +277,8 @@ public class WorkingDir { /** * @param src not a directory, must exist - * @param dest not a directory, will be overwritten if existing - * @@reurn true if it was copied successfully + * @param dst not a directory, will be overwritten if existing + * @return true if it was copied successfully */ public static boolean copyFile(File src, File dst) { if (!src.exists()) return false;