diff --git a/LICENSE.txt b/LICENSE.txt index 02b2862d8..f68af6e0c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -179,6 +179,10 @@ distributions. See the source package for the additional license information. Atalk: Public domain + Desktopgui + Copyright (c) Mathias De Maré + See apps/desktopgui/LICENSE + SAM C Library: Copyright (c) 2004, Matthew P. Cashdollar See apps/sam/c/doc/license.txt diff --git a/apps/BOB/src/net/i2p/BOB/BOB.java b/apps/BOB/src/net/i2p/BOB/BOB.java index 830808a54..9f87e4f88 100644 --- a/apps/BOB/src/net/i2p/BOB/BOB.java +++ b/apps/BOB/src/net/i2p/BOB/BOB.java @@ -27,14 +27,18 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.net.ConnectException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketTimeoutException; import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; import net.i2p.client.I2PClient; import net.i2p.client.streaming.RetransmissionTimer; import net.i2p.util.Log; import net.i2p.util.SimpleScheduler; +import net.i2p.util.SimpleStore; import net.i2p.util.SimpleTimer2; /** @@ -114,10 +118,15 @@ public class BOB { public final static String PROP_CONFIG_LOCATION = "BOB.config"; public final static String PROP_BOB_PORT = "BOB.port"; public final static String PROP_BOB_HOST = "BOB.host"; - private static int maxConnections = 0; private static NamedDB database; private static Properties props = new Properties(); - + private static AtomicBoolean spin = new AtomicBoolean(true); + private static final String P_RUNNING = "RUNNING"; + private static final String P_STARTING = "STARTING"; + private static final String P_STOPPING = "STOPPING"; + private static AtomicBoolean lock = new AtomicBoolean(false); + // no longer used. + // private static int maxConnections = 0; /** * Log a warning @@ -149,6 +158,12 @@ public class BOB { _log.error(arg); } + /** + * Stop BOB gracefully + */ + public static void stop() { + spin.set(false); + } /** * Listen for incoming connections and handle them * @@ -156,6 +171,7 @@ public class BOB { */ public static void main(String[] args) { database = new NamedDB(); + ServerSocket listener = null; int i = 0; boolean save = false; // Set up all defaults to be passed forward to other threads. @@ -168,77 +184,174 @@ public class BOB { i = Y.hashCode(); i = Y1.hashCode(); i = Y2.hashCode(); - - { - try { - FileInputStream fi = new FileInputStream(configLocation); - props.load(fi); - fi.close(); - } catch(FileNotFoundException fnfe) { - warn("Unable to load up the BOB config file " + configLocation + ", Using defaults."); - warn(fnfe.toString()); - save = true; - } catch(IOException ioe) { - warn("IOException on BOB config file " + configLocation + ", using defaults."); - warn(ioe.toString()); - } - } - // Global router and client API configurations that are missing are set to defaults here. - if(!props.containsKey(I2PClient.PROP_TCP_HOST)) { - props.setProperty(I2PClient.PROP_TCP_HOST, "localhost"); - } - if(!props.containsKey(I2PClient.PROP_TCP_PORT)) { - props.setProperty(I2PClient.PROP_TCP_PORT, "7654"); - } - if(!props.containsKey(I2PClient.PROP_RELIABILITY)) { - props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT); - } - if(!props.containsKey(PROP_BOB_PORT)) { - props.setProperty(PROP_BOB_PORT, "2827"); // 0xB0B - } - if(!props.containsKey("inbound.length")) { - props.setProperty("inbound.length", "1"); - } - if(!props.containsKey("outbound.length")) { - props.setProperty("outbound.length", "1"); - } - if(!props.containsKey("inbound.lengthVariance")) { - props.setProperty("inbound.lengthVariance", "0"); - } - if(!props.containsKey("outbound.lengthVariance")) { - props.setProperty("outbound.lengthVariance", "0"); - } - if(!props.containsKey(PROP_BOB_HOST)) { - props.setProperty(PROP_BOB_HOST, "localhost"); - } - if(save) { - try { - warn("Writing new defaults file " + configLocation); - FileOutputStream fo = new FileOutputStream(configLocation); - props.store(fo, configLocation); - fo.close(); - } catch(IOException ioe) { - error("IOException on BOB config file " + configLocation + ", " + ioe); - } - } - - i = 0; try { - info("BOB is now running."); - ServerSocket listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST))); - Socket server; - - while((i++ < maxConnections) || (maxConnections == 0)) { - //DoCMDS connection; - - server = listener.accept(); - DoCMDS conn_c = new DoCMDS(server, props, database, _log); - Thread t = new Thread(conn_c); - t.start(); + { + try { + FileInputStream fi = new FileInputStream(configLocation); + props.load(fi); + fi.close(); + } catch (FileNotFoundException fnfe) { + warn("Unable to load up the BOB config file " + configLocation + ", Using defaults."); + warn(fnfe.toString()); + save = true; + } catch (IOException ioe) { + warn("IOException on BOB config file " + configLocation + ", using defaults."); + warn(ioe.toString()); + } } - } catch(IOException ioe) { - error("IOException on socket listen: " + ioe); - ioe.printStackTrace(); + // Global router and client API configurations that are missing are set to defaults here. + if (!props.containsKey(I2PClient.PROP_TCP_HOST)) { + props.setProperty(I2PClient.PROP_TCP_HOST, "localhost"); + } + if (!props.containsKey(I2PClient.PROP_TCP_PORT)) { + props.setProperty(I2PClient.PROP_TCP_PORT, "7654"); + } + if (!props.containsKey(I2PClient.PROP_RELIABILITY)) { + props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT); + } + if (!props.containsKey(PROP_BOB_PORT)) { + props.setProperty(PROP_BOB_PORT, "2827"); // 0xB0B + } + if (!props.containsKey("inbound.length")) { + props.setProperty("inbound.length", "1"); + } + if (!props.containsKey("outbound.length")) { + props.setProperty("outbound.length", "1"); + } + if (!props.containsKey("inbound.lengthVariance")) { + props.setProperty("inbound.lengthVariance", "0"); + } + if (!props.containsKey("outbound.lengthVariance")) { + props.setProperty("outbound.lengthVariance", "0"); + } + if (!props.containsKey(PROP_BOB_HOST)) { + props.setProperty(PROP_BOB_HOST, "localhost"); + } + if (save) { + try { + warn("Writing new defaults file " + configLocation); + FileOutputStream fo = new FileOutputStream(configLocation); + props.store(fo, configLocation); + fo.close(); + } catch (IOException ioe) { + error("IOException on BOB config file " + configLocation + ", " + ioe); + } + } + + i = 0; + boolean g = false; + try { + info("BOB is now running."); + listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST))); + Socket server = null; + listener.setSoTimeout(500); // .5 sec + while (spin.get()) { + //DoCMDS connection; + + try { + server = listener.accept(); + g = true; + } catch (ConnectException ce) { + g = false; + } catch (SocketTimeoutException ste) { + g = false; + } + + if (g) { + DoCMDS conn_c = new DoCMDS(spin, lock, server, props, database, _log); + Thread t = new Thread(conn_c); + t.setName("BOB.DoCMDS " + i); + t.start(); + i++; + } + } + } catch (IOException ioe) { + error("IOException on socket listen: " + ioe); + ioe.printStackTrace(); + } + } finally { + info("BOB is now shutting down..."); + // Clean up everything. + try { + listener.close(); + } catch (Exception ex) { + // nop + } + // Find all our "BOB.DoCMDS" threads, wait for them to be finished. + // We could order them to stop, but that could cause nasty issues in the locks. + visitAllThreads(); + database.getReadLock(); + int all = database.getcount(); + database.releaseReadLock(); + NamedDB nickinfo; + for (i = 0; i < all; i++) { + database.getReadLock(); + nickinfo = (NamedDB) database.getnext(i); + nickinfo.getReadLock(); + if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) { + nickinfo.releaseReadLock(); + database.releaseReadLock(); + database.getWriteLock(); + nickinfo.getWriteLock(); + nickinfo.add(P_STOPPING, new Boolean(true)); + nickinfo.releaseWriteLock(); + database.releaseWriteLock(); + } else { + nickinfo.releaseReadLock(); + database.releaseReadLock(); + } + } + info("BOB is now stopped."); + + } + } + + /** + * Find the root thread group, + * then find all theads with certain names and wait for them all to be dead. + * + */ + private static void visitAllThreads() { + ThreadGroup root = Thread.currentThread().getThreadGroup().getParent(); + while (root.getParent() != null) { + root = root.getParent(); + } + + // Visit each thread group + waitjoin(root, 0, root.getName()); + } + + private static void waitjoin(ThreadGroup group, int level, String tn) { + // Get threads in `group' + int numThreads = group.activeCount(); + Thread[] threads = new Thread[numThreads * 2]; + numThreads = group.enumerate(threads, false); + // Enumerate each thread in `group' and wait for it to stop if it is one of ours. + for (int i = 0; i < numThreads; i++) { + // Get thread + Thread thread = threads[i]; + if (thread.getName().startsWith("BOB.DoCMDS ")) { + try { + if (thread.isAlive()) { + try { + thread.join(); + } catch (InterruptedException ex) { + } + } + } catch (SecurityException se) { + //nop + } + } + } + + // Get thread subgroups of `group' + int numGroups = group.activeGroupCount(); + ThreadGroup[] groups = new ThreadGroup[numGroups * 2]; + numGroups = group.enumerate(groups, false); + + // Recursively visit each subgroup + for (int i = 0; i < numGroups; i++) { + waitjoin(groups[i], level + 1, groups[i].getName()); } } } diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java index 4a13844cb..c49be9826 100644 --- a/apps/BOB/src/net/i2p/BOB/DoCMDS.java +++ b/apps/BOB/src/net/i2p/BOB/DoCMDS.java @@ -31,10 +31,12 @@ import java.io.PrintStream; import java.net.Socket; import java.util.Properties; import java.util.StringTokenizer; +import java.util.concurrent.atomic.AtomicBoolean; import net.i2p.I2PException; import net.i2p.client.I2PClientFactory; import net.i2p.data.Destination; import net.i2p.util.Log; +import net.i2p.util.SimpleStore; /** * Simplistic command parser for BOB @@ -57,6 +59,8 @@ public class DoCMDS implements Runnable { private boolean dk, ns, ip, op; private NamedDB nickinfo; private Log _log; + private AtomicBoolean LIVE; + private AtomicBoolean lock; /* database strings */ private static final String P_DEST = "DESTINATION"; private static final String P_INHOST = "INHOST"; @@ -94,6 +98,7 @@ public class DoCMDS implements Runnable { private static final String C_status = "status"; private static final String C_stop = "stop"; private static final String C_verify = "verify"; + private static final String C_zap = "zap"; /* all the coomands available, plus description */ private static final String C_ALL[][] = { @@ -119,6 +124,7 @@ public class DoCMDS implements Runnable { {C_status, C_status + " nickname * Display status of a nicknamed tunnel."}, {C_stop, C_stop + " * Stops the current nicknamed tunnel."}, {C_verify, C_verify + " BASE64_key * Verifies BASE64 destination."}, + {C_zap, C_zap + " * Shuts down BOB."}, {"", "COMMANDS: " + // this is ugly, but... C_help + " " + C_clear + " " + @@ -141,19 +147,22 @@ public class DoCMDS implements Runnable { C_start + " " + C_status + " " + C_stop + " " + - C_verify + C_verify + " " + + C_zap }, {" ", " "} // end of list }; /** - * + * @parm LIVE * @param server * @param props * @param database * @param _log */ - DoCMDS(Socket server, Properties props, NamedDB database, Log _log) { + DoCMDS(AtomicBoolean LIVE, AtomicBoolean lock, Socket server, Properties props, NamedDB database, Log _log) { + this.lock = lock; + this.LIVE = LIVE; this.server = server; this.props = new Properties(); this.database = database; @@ -509,6 +518,11 @@ public class DoCMDS implements Runnable { } else if (Command.equals(C_quit)) { // End the command session break quit; + } else if (Command.equals(C_zap)) { + // Kill BOB!! (let's hope this works!) + LIVE.set(false); + // End the command session + break quit; } else if (Command.equals(C_newkeys)) { if (ns) { try { @@ -1260,7 +1274,10 @@ public class DoCMDS implements Runnable { } else { MUXlisten tunnel; try { - tunnel = new MUXlisten(database, nickinfo, _log); + while(!lock.compareAndSet(false, true)) { + // wait + } + tunnel = new MUXlisten(lock, database, nickinfo, _log); Thread t = new Thread(tunnel); t.start(); // try { @@ -1270,8 +1287,10 @@ public class DoCMDS implements Runnable { // } out.println("OK tunnel starting"); } catch (I2PException e) { + lock.set(false); out.println("ERROR starting tunnel: " + e); } catch (IOException e) { + lock.set(false); out.println("ERROR starting tunnel: " + e); } } diff --git a/apps/BOB/src/net/i2p/BOB/I2Plistener.java b/apps/BOB/src/net/i2p/BOB/I2Plistener.java index caaadc76d..b8d9145f2 100644 --- a/apps/BOB/src/net/i2p/BOB/I2Plistener.java +++ b/apps/BOB/src/net/i2p/BOB/I2Plistener.java @@ -25,8 +25,6 @@ package net.i2p.BOB; import java.net.ConnectException; import java.net.SocketTimeoutException; -import java.util.logging.Level; -import java.util.logging.Logger; import net.i2p.I2PException; import net.i2p.client.streaming.I2PServerSocket; import net.i2p.client.streaming.I2PSocket; diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java index f77d5bc82..5f6885dd5 100644 --- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java +++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; import net.i2p.I2PException; import net.i2p.client.streaming.I2PServerSocket; import net.i2p.client.streaming.I2PSocketManager; @@ -52,6 +53,7 @@ public class MUXlisten implements Runnable { private int backlog = 50; // should this be more? less? boolean go_out; boolean come_in; + private AtomicBoolean lock; /** * Constructor Will fail if INPORT is occupied. @@ -62,9 +64,10 @@ public class MUXlisten implements Runnable { * @throws net.i2p.I2PException * @throws java.io.IOException */ - MUXlisten(NamedDB database, NamedDB info, Log _log) throws I2PException, IOException, RuntimeException { + MUXlisten(AtomicBoolean lock, NamedDB database, NamedDB info, Log _log) throws I2PException, IOException, RuntimeException { int port = 0; InetAddress host = null; + this.lock = lock; this.tg = null; this.database = database; this.info = info; @@ -151,7 +154,7 @@ public class MUXlisten implements Runnable { return; } // socketManager.addDisconnectListener(new DisconnectListener()); - + lock.set(false); quit: { try { @@ -216,7 +219,7 @@ public class MUXlisten implements Runnable { break die; } } - + /* cleared in the finally... try { wlock(); try { @@ -233,6 +236,7 @@ public class MUXlisten implements Runnable { } catch (Exception e) { break die; } + */ } // die } catch (Exception e) { @@ -241,11 +245,11 @@ public class MUXlisten implements Runnable { } } // quit } finally { - // Start cleanup. Allow threads above this one to catch the stop signal. - try { - Thread.sleep(250); - } catch (InterruptedException ex) { + // Start cleanup. + while (!lock.compareAndSet(false, true)) { + // wait } + // zero out everything. try { wlock(); @@ -261,7 +265,6 @@ public class MUXlisten implements Runnable { } catch (Exception e) { } - if (SS != null) { try { SS.close(); @@ -279,7 +282,14 @@ public class MUXlisten implements Runnable { socketManager.destroySocketManager(); } catch (Exception e) { // nop - } + } + // Some grace time. + try { + Thread.sleep(250); + } catch (InterruptedException ex) { + } + + lock.set(false); // Should we force waiting for all threads?? // Wait around till all threads are collected. if (tg != null) { String boner = tg.getName(); @@ -362,39 +372,41 @@ public class MUXlisten implements Runnable { } } + /* private static void nuke(ThreadGroup group, int level) { - // Get threads in `group' - int numThreads = group.activeCount(); - Thread[] threads = new Thread[numThreads * 2]; - numThreads = group.enumerate(threads, false); - // Enumerate each thread in `group' and stop it. - for (int i = 0; i < numThreads; i++) { - // Get thread - Thread thread = threads[i]; - try { - if (thread.isAlive()) { - thread.stop(); - } - } catch (SecurityException se) { - //nop - } - } - - // Get thread subgroups of `group' - int numGroups = group.activeGroupCount(); - ThreadGroup[] groups = new ThreadGroup[numGroups * 2]; - numGroups = group.enumerate(groups, false); - - // Recursively visit each subgroup - for (int i = 0; i < numGroups; i++) { - nuke(groups[i], level + 1); - } - try { - group.destroy(); - } catch (IllegalThreadStateException IE) { - //nop - } catch (SecurityException se) { - //nop - } + // Get threads in `group' + int numThreads = group.activeCount(); + Thread[] threads = new Thread[numThreads * 2]; + numThreads = group.enumerate(threads, false); + // Enumerate each thread in `group' and stop it. + for (int i = 0; i < numThreads; i++) { + // Get thread + Thread thread = threads[i]; + try { + if (thread.isAlive()) { + thread.stop(); } + } catch (SecurityException se) { + //nop + } + } + + // Get thread subgroups of `group' + int numGroups = group.activeGroupCount(); + ThreadGroup[] groups = new ThreadGroup[numGroups * 2]; + numGroups = group.enumerate(groups, false); + + // Recursively visit each subgroup + for (int i = 0; i < numGroups; i++) { + nuke(groups[i], level + 1); + } + try { + group.destroy(); + } catch (IllegalThreadStateException IE) { + //nop + } catch (SecurityException se) { + //nop + } + } + */ } diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java index d92a5cef0..99d408c43 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPio.java +++ b/apps/BOB/src/net/i2p/BOB/TCPio.java @@ -49,8 +49,8 @@ public class TCPio implements Runnable { TCPio(InputStream Ain, OutputStream Aout /*, NamedDB info , NamedDB database */) { this.Ain = Ain; this.Aout = Aout; - // this.info = info; - // this.database = database; + // this.info = info; + // this.database = database; } /** @@ -86,30 +86,35 @@ public class TCPio implements Runnable { byte a[] = new byte[1]; boolean spin = true; try { - while(spin) { - b = Ain.read(a, 0, 1); - if(b > 0) { - Aout.write(a, 0, b); - } else if(b == 0) { - Thread.yield(); // this should act like a mini sleep. - if(Ain.available() == 0) { + try { + while (spin) { + b = Ain.read(a, 0, 1); + if (b > 0) { + Aout.write(a, 0, b); + } else if (b == 0) { + Thread.yield(); // this should act like a mini sleep. + if (Ain.available() == 0) { Thread.sleep(10); + } + } else { + /* according to the specs: + * + * The total number of bytes read into the buffer, + * or -1 if there is no more data because the end of + * the stream has been reached. + * + */ + // System.out.println("TCPio: End Of Stream"); + // Ain.close(); + // Aout.close(); + //return; + break; } - } else { - /* according to the specs: - * - * The total number of bytes read into the buffer, - * or -1 if there is no more data because the end of - * the stream has been reached. - * - */ - // System.out.println("TCPio: End Of Stream"); - Ain.close(); - Aout.close(); - return; } + } catch (Exception e) { } - } catch(Exception e) { + // System.out.println("TCPio: Leaving."); + } finally { // Eject!!! Eject!!! //System.out.println("TCPio: Caught an exception " + e); try { @@ -122,6 +127,5 @@ public class TCPio implements Runnable { } return; } - // System.out.println("TCPio: Leaving."); } } diff --git a/apps/BOB/src/net/i2p/BOB/TCPlistener.java b/apps/BOB/src/net/i2p/BOB/TCPlistener.java index 0ac67d277..8f559c09f 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPlistener.java +++ b/apps/BOB/src/net/i2p/BOB/TCPlistener.java @@ -29,8 +29,6 @@ import java.net.Socket; import java.net.SocketTimeoutException; // import net.i2p.client.I2PSession; // import net.i2p.client.I2PSessionException; -import java.util.logging.Level; -import java.util.logging.Logger; import net.i2p.client.streaming.I2PServerSocket; import net.i2p.client.streaming.I2PSocketManager; import net.i2p.util.Log; @@ -142,12 +140,7 @@ public class TCPlistener implements Runnable { g = false; } } - listener.close(); } catch (IOException ioe) { - try { - listener.close(); - } catch (IOException e) { - } } } } finally { diff --git a/apps/desktopgui/build.xml b/apps/desktopgui/build.xml index 77bd6d778..795edf046 100644 --- a/apps/desktopgui/build.xml +++ b/apps/desktopgui/build.xml @@ -76,10 +76,12 @@ + + diff --git a/apps/desktopgui/nbproject/project.properties b/apps/desktopgui/nbproject/project.properties index e9f5d1a2a..fb880932f 100644 --- a/apps/desktopgui/nbproject/project.properties +++ b/apps/desktopgui/nbproject/project.properties @@ -23,6 +23,7 @@ excludes= file.reference.appframework.jar=lib/appframework.jar file.reference.i2p.jar=../../core/java/build/i2p.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 includes=** jar.compress=false @@ -30,7 +31,8 @@ javac.classpath=\ ${file.reference.router.jar}:\ ${file.reference.appframework.jar}:\ ${file.reference.swing-worker.jar}:\ - ${file.reference.i2p.jar} + ${file.reference.i2p.jar}:\ + ${file.reference.routerconsole.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 f4e948e52..10a6e5293 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.2"; + public static final String VERSION = "0.0.1.3"; } diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.form b/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.form index 7c0fc8edc..4c7cb5afc 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.form +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.form @@ -2,7 +2,7 @@
- + @@ -81,12 +81,18 @@ + + + + + + @@ -141,7 +147,7 @@ - + @@ -155,7 +161,7 @@ - + @@ -201,10 +207,10 @@ - + - - + + @@ -212,10 +218,10 @@ - + - - + + @@ -229,7 +235,7 @@ - + @@ -243,7 +249,7 @@ - + @@ -251,10 +257,10 @@ - + - - + + @@ -262,10 +268,10 @@ - + - - + + @@ -273,10 +279,10 @@ - + - - + + @@ -365,7 +371,7 @@ - + @@ -374,7 +380,7 @@ - + @@ -383,7 +389,7 @@ - + @@ -394,18 +400,27 @@ + + + + + + + + + diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.java b/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.java index 910ca21fb..474e39473 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.java +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/GeneralConfiguration.java @@ -6,7 +6,23 @@ package net.i2p.desktopgui.gui; +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.logging.Level; +import java.util.logging.Logger; import net.i2p.desktopgui.router.configuration.SpeedHelper; +import javax.swing.JComboBox; +import javax.swing.ButtonModel; +import javax.swing.JTextField; +import net.i2p.desktopgui.router.RouterHelper; +import net.i2p.desktopgui.router.configuration.SpeedHandler; +import net.i2p.desktopgui.router.configuration.UpdateHelper; +import net.i2p.router.web.NewsFetcher; +import net.i2p.desktopgui.router.configuration.UpdateHandler; +import java.util.Date; +import javax.swing.SwingWorker; /** * @@ -19,11 +35,56 @@ public class GeneralConfiguration extends javax.swing.JFrame { initComponents(); extraInitComponents(); this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + this.setLocationRelativeTo(null); + this.requestFocus(); this.setVisible(true); } private void extraInitComponents() { - downloadspeed.setText(SpeedHelper.getInboundBandwidth()); + initSpeedTab(); + initUpdateTab(); + } + + private void initSpeedTab() { + try { + String inbound = SpeedHelper.getInboundBandwidth(); + String outbound = SpeedHelper.getOutboundBandwidth(); + + initSpeeds("" + Integer.parseInt(inbound)*8, "" + Integer.parseInt(outbound)*8); + initUsage("" + Integer.parseInt(inbound), "" + Integer.parseInt(outbound)); + } + catch(Exception e) { + e.printStackTrace(); + System.out.println("Exception noticed, probably running desktopgui in a debugger instead of along with I2P!?"); + initSpeeds("100", "100"); + initUsage("12", "12"); + } + } + + private void initUpdateTab() { + //Set update policy + String updatePolicy = UpdateHelper.getUpdatePolicy(); + if(updatePolicy.equals(UpdateHelper.NOTIFY_UPDATE_POLICY)) { + updateButtonGroup.setSelected(updateInform.getModel(), true); + } + else if(updatePolicy.equals(UpdateHelper.DOWNLOAD_UPDATE_POLICY)) { + updateButtonGroup.setSelected(updateDownload.getModel(), true); + } + else if(updatePolicy.equals(UpdateHelper.INSTALL_UPDATE_POLICY)) { + updateButtonGroup.setSelected(updateDownloadRestart.getModel(), true); + } + else { + System.out.println("desktopgui: no updates for you!"); + } + + //Check if an update is available + //TODO: move this method out of the routerconsole so desktopgui doesn't depend on routerconsole!!! + if(NewsFetcher.getInstance(RouterHelper.getContext()).updateAvailable()) { + updateNow.setVisible(true); + } + else { + updateNow.setVisible(false); + } } /** This method is called from within the constructor to @@ -35,7 +96,7 @@ public class GeneralConfiguration extends javax.swing.JFrame { // //GEN-BEGIN:initComponents private void initComponents() { - buttonGroup1 = new javax.swing.ButtonGroup(); + updateButtonGroup = new javax.swing.ButtonGroup(); applyPanel = new javax.swing.JPanel(); cancel = new javax.swing.JToggleButton(); ok = new javax.swing.JToggleButton(); @@ -47,13 +108,13 @@ public class GeneralConfiguration extends javax.swing.JFrame { downloadspeed = new javax.swing.JTextField(); uploadkbps = new javax.swing.JComboBox(); downloadkbps = new javax.swing.JComboBox(); - jLabel3 = new javax.swing.JLabel(); - jLabel4 = new javax.swing.JLabel(); + uploadUsageLabel = new javax.swing.JLabel(); + downloadUsageLabel = new javax.swing.JLabel(); uploadgb = new javax.swing.JTextField(); downloadgb = new javax.swing.JTextField(); - jLabel5 = new javax.swing.JLabel(); - jLabel6 = new javax.swing.JLabel(); - jLabel7 = new javax.swing.JLabel(); + gbUploadLabel = new javax.swing.JLabel(); + gbDownloadLabel = new javax.swing.JLabel(); + uploadDownloadExplanation = new javax.swing.JLabel(); updatesPanel = new javax.swing.JPanel(); updateMethod = new javax.swing.JLabel(); updateInform = new javax.swing.JRadioButton(); @@ -80,9 +141,19 @@ public class GeneralConfiguration extends javax.swing.JFrame { cancel.setText(resourceMap.getString("cancel.text")); // NOI18N cancel.setName("cancel"); // NOI18N + cancel.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + cancelMouseClicked(evt); + } + }); ok.setText(resourceMap.getString("ok.text")); // NOI18N ok.setName("ok"); // NOI18N + ok.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + okMouseClicked(evt); + } + }); javax.swing.GroupLayout applyPanelLayout = new javax.swing.GroupLayout(applyPanel); applyPanel.setLayout(applyPanelLayout); @@ -122,8 +193,8 @@ public class GeneralConfiguration extends javax.swing.JFrame { uploadspeed.setText(resourceMap.getString("uploadspeed.text")); // NOI18N uploadspeed.setName("uploadspeed"); // NOI18N uploadspeed.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyTyped(java.awt.event.KeyEvent evt) { - speedKeyTyped(evt); + public void keyReleased(java.awt.event.KeyEvent evt) { + speedKeyReleased(evt); } }); speedPanel.add(uploadspeed); @@ -132,8 +203,8 @@ public class GeneralConfiguration extends javax.swing.JFrame { downloadspeed.setText(resourceMap.getString("downloadspeed.text")); // NOI18N downloadspeed.setName("downloadspeed"); // NOI18N downloadspeed.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyTyped(java.awt.event.KeyEvent evt) { - speedKeyTyped(evt); + public void keyReleased(java.awt.event.KeyEvent evt) { + speedKeyReleased(evt); } }); speedPanel.add(downloadspeed); @@ -159,21 +230,21 @@ public class GeneralConfiguration extends javax.swing.JFrame { speedPanel.add(downloadkbps); downloadkbps.setBounds(240, 60, 68, 27); - jLabel3.setText(resourceMap.getString("jLabel3.text")); // NOI18N - jLabel3.setName("jLabel3"); // NOI18N - speedPanel.add(jLabel3); - jLabel3.setBounds(330, 20, 97, 30); + uploadUsageLabel.setText(resourceMap.getString("uploadUsageLabel.text")); // NOI18N + uploadUsageLabel.setName("uploadUsageLabel"); // NOI18N + speedPanel.add(uploadUsageLabel); + uploadUsageLabel.setBounds(330, 20, 97, 30); - jLabel4.setText(resourceMap.getString("jLabel4.text")); // NOI18N - jLabel4.setName("jLabel4"); // NOI18N - speedPanel.add(jLabel4); - jLabel4.setBounds(330, 60, 97, 30); + downloadUsageLabel.setText(resourceMap.getString("downloadUsageLabel.text")); // NOI18N + downloadUsageLabel.setName("downloadUsageLabel"); // NOI18N + speedPanel.add(downloadUsageLabel); + downloadUsageLabel.setBounds(330, 60, 97, 30); uploadgb.setText(resourceMap.getString("uploadgb.text")); // NOI18N uploadgb.setName("uploadgb"); // NOI18N uploadgb.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyTyped(java.awt.event.KeyEvent evt) { - uploadgbKeyTyped(evt); + public void keyReleased(java.awt.event.KeyEvent evt) { + monthKeyReleased(evt); } }); speedPanel.add(uploadgb); @@ -182,27 +253,27 @@ public class GeneralConfiguration extends javax.swing.JFrame { downloadgb.setText(resourceMap.getString("downloadgb.text")); // NOI18N downloadgb.setName("downloadgb"); // NOI18N downloadgb.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyTyped(java.awt.event.KeyEvent evt) { - downloadgbKeyTyped(evt); + public void keyReleased(java.awt.event.KeyEvent evt) { + monthKeyReleased(evt); } }); speedPanel.add(downloadgb); downloadgb.setBounds(440, 60, 60, 27); - jLabel5.setText(resourceMap.getString("jLabel5.text")); // NOI18N - jLabel5.setName("jLabel5"); // NOI18N - speedPanel.add(jLabel5); - jLabel5.setBounds(510, 20, 19, 30); + gbUploadLabel.setText(resourceMap.getString("gbUploadLabel.text")); // NOI18N + gbUploadLabel.setName("gbUploadLabel"); // NOI18N + speedPanel.add(gbUploadLabel); + gbUploadLabel.setBounds(510, 20, 19, 30); - jLabel6.setText(resourceMap.getString("jLabel6.text")); // NOI18N - jLabel6.setName("jLabel6"); // NOI18N - speedPanel.add(jLabel6); - jLabel6.setBounds(510, 60, 19, 30); + gbDownloadLabel.setText(resourceMap.getString("gbDownloadLabel.text")); // NOI18N + gbDownloadLabel.setName("gbDownloadLabel"); // NOI18N + speedPanel.add(gbDownloadLabel); + gbDownloadLabel.setBounds(510, 60, 19, 30); - jLabel7.setText(resourceMap.getString("jLabel7.text")); // NOI18N - jLabel7.setName("jLabel7"); // NOI18N - speedPanel.add(jLabel7); - jLabel7.setBounds(20, 100, 520, 70); + uploadDownloadExplanation.setText(resourceMap.getString("uploadDownloadExplanation.text")); // NOI18N + uploadDownloadExplanation.setName("uploadDownloadExplanation"); // NOI18N + speedPanel.add(uploadDownloadExplanation); + uploadDownloadExplanation.setBounds(20, 100, 520, 70); settingsPanel.addTab(resourceMap.getString("speedPanel.TabConstraints.tabTitle"), speedPanel); // NOI18N @@ -211,26 +282,41 @@ public class GeneralConfiguration extends javax.swing.JFrame { updateMethod.setText(resourceMap.getString("updateMethod.text")); // NOI18N updateMethod.setName("updateMethod"); // NOI18N - buttonGroup1.add(updateInform); + updateButtonGroup.add(updateInform); updateInform.setText(resourceMap.getString("updateInform.text")); // NOI18N updateInform.setName("updateInform"); // NOI18N - buttonGroup1.add(updateDownload); + updateButtonGroup.add(updateDownload); updateDownload.setText(resourceMap.getString("updateDownload.text")); // NOI18N updateDownload.setName("updateDownload"); // NOI18N - buttonGroup1.add(updateDownloadRestart); + updateButtonGroup.add(updateDownloadRestart); updateDownloadRestart.setText(resourceMap.getString("updateDownloadRestart.text")); // NOI18N updateDownloadRestart.setName("updateDownloadRestart"); // NOI18N checkUpdates.setText(resourceMap.getString("checkUpdates.text")); // NOI18N checkUpdates.setName("checkUpdates"); // NOI18N + checkUpdates.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + checkUpdatesActionPerformed(evt); + } + }); updateNow.setText(resourceMap.getString("updateNow.text")); // NOI18N updateNow.setName("updateNow"); // NOI18N + updateNow.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + updateNowActionPerformed(evt); + } + }); advancedUpdateConfig.setText(resourceMap.getString("advancedUpdateConfig.text")); // NOI18N advancedUpdateConfig.setName("advancedUpdateConfig"); // NOI18N + advancedUpdateConfig.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + advancedUpdateConfigActionPerformed(evt); + } + }); javax.swing.GroupLayout updatesPanelLayout = new javax.swing.GroupLayout(updatesPanel); updatesPanel.setLayout(updatesPanelLayout); @@ -377,7 +463,7 @@ public class GeneralConfiguration extends javax.swing.JFrame { pack(); }// //GEN-END:initComponents -private void speedKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_speedKeyTyped + private void speedKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_speedKeyReleased try { String upload = ""; if(uploadkbps.getSelectedIndex() == KILOBIT) @@ -395,47 +481,179 @@ private void speedKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_speed e.printStackTrace(); return; } -}//GEN-LAST:event_speedKeyTyped +}//GEN-LAST:event_speedKeyReleased private void uploadkbpsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_uploadkbpsActionPerformed - // TODO add your handling code here: + kbpsSwitchPerformed(uploadkbps, uploadspeed); }//GEN-LAST:event_uploadkbpsActionPerformed private void downloadkbpsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_downloadkbpsActionPerformed - // TODO add your handling code here: + kbpsSwitchPerformed(downloadkbps, downloadspeed); }//GEN-LAST:event_downloadkbpsActionPerformed -private void uploadgbKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_uploadgbKeyTyped - // TODO add your handling code here: -}//GEN-LAST:event_uploadgbKeyTyped +private void monthKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_monthKeyReleased + try { + int uploadMonthValue = Integer.parseInt(uploadgb.getText()); + int downloadMonthValue = Integer.parseInt(downloadgb.getText()); -private void downloadgbKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_downloadgbKeyTyped - // TODO add your handling code here: -}//GEN-LAST:event_downloadgbKeyTyped + String upload = ""; + String burstUpload = ""; + String download = ""; + String burstDownload = ""; + + if(uploadkbps.getSelectedIndex() == KILOBIT) + upload = "" + SpeedHelper.calculateSpeed(uploadMonthValue)*8; //kbit + else + upload = "" + SpeedHelper.calculateSpeed(uploadMonthValue); //kbyte + + if(downloadkbps.getSelectedIndex() == KILOBIT) + download = "" + SpeedHelper.calculateSpeed(downloadMonthValue)*8; //kbit + else + download = "" + SpeedHelper.calculateSpeed(downloadMonthValue); //kbyte + + initSpeeds(upload, download); + } + catch(NumberFormatException e) { + e.printStackTrace(); + return; + } +}//GEN-LAST:event_monthKeyReleased + +private void cancelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cancelMouseClicked + this.dispose(); +}//GEN-LAST:event_cancelMouseClicked + +private void okMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_okMouseClicked + saveSpeeds(); + saveUpdatePolicy(); + this.dispose(); +}//GEN-LAST:event_okMouseClicked + +private void checkUpdatesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkUpdatesActionPerformed + long current = new Date().getTime(); + if(current < newsLastFetched + 5*60*1000) { + return; + } + checkUpdates.setText("Checking for updates"); + checkUpdates.setEnabled(false); + newsLastFetched = current; + SwingWorker sw = new SwingWorker() { + + @Override + protected Object doInBackground() throws Exception { + NewsFetcher.getInstance(RouterHelper.getContext()).fetchNews(); + return null; + } + + @Override + protected void done() { + checkUpdates.setText("Check for updates now"); + checkUpdates.setEnabled(true); + if(NewsFetcher.getInstance(RouterHelper.getContext()).updateAvailable()) { + updateNow.setVisible(true); + } + } + + }; +}//GEN-LAST:event_checkUpdatesActionPerformed + +private void updateNowActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_updateNowActionPerformed + SwingWorker sw = new SwingWorker() { + + @Override + protected Object doInBackground() throws Exception { + new net.i2p.router.web.UpdateHandler().update(); + return null; + } + + }; + updateNow.setEnabled(false); + updateNow.setText("Updating..."); + checkUpdates.setEnabled(false); + +}//GEN-LAST:event_updateNowActionPerformed + +private void advancedUpdateConfigActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_advancedUpdateConfigActionPerformed + try { + Desktop.getDesktop().browse(new URI("http://127.0.0.1:7657/configupdate.jsp")); + } catch (URISyntaxException ex) { + Logger.getLogger(GeneralConfiguration.class.getName()).log(Level.SEVERE, null, ex); + } + catch (IOException ex) { + Logger.getLogger(GeneralConfiguration.class.getName()).log(Level.SEVERE, null, ex); + } +}//GEN-LAST:event_advancedUpdateConfigActionPerformed protected void initUsage(String upload, String download) { uploadgb.setText("" + SpeedHelper.calculateMonthlyUsage(Integer.parseInt(upload))); downloadgb.setText("" + SpeedHelper.calculateMonthlyUsage(Integer.parseInt(download))); } + protected void initSpeeds(String upload, String download) { + uploadspeed.setText(upload); + downloadspeed.setText(download); + } + + private void kbpsSwitchPerformed(JComboBox kbps, JTextField speed) { + int index = kbps.getSelectedIndex(); + int previous = Integer.parseInt(speed.getText()); + if(index == KILOBIT) { + speed.setText("" + previous*8); + } + else { + speed.setText("" + previous/8); + } + } + + protected void saveSpeeds() { + int maxDownload = Integer.parseInt(downloadspeed.getText()); + int maxUpload = Integer.parseInt(uploadspeed.getText()); + if(uploadkbps.getSelectedIndex() == KILOBIT) { + SpeedHandler.setOutboundBandwidth(maxUpload/8); + SpeedHandler.setOutboundBurstBandwidth(maxUpload/8); + } + else { + SpeedHandler.setOutboundBandwidth(maxUpload); + SpeedHandler.setOutboundBurstBandwidth(maxUpload); + } + if(downloadkbps.getSelectedIndex() == KILOBIT) { + SpeedHandler.setInboundBandwidth(maxDownload/8); + SpeedHandler.setInboundBurstBandwidth(maxDownload/8); + } + else { + SpeedHandler.setInboundBandwidth(maxDownload); + SpeedHandler.setInboundBurstBandwidth(maxDownload); + } + } + + protected void saveUpdatePolicy() { + ButtonModel policyButton = updateButtonGroup.getSelection(); + if(policyButton.equals(updateInform.getModel())) { + UpdateHandler.setUpdatePolicy(UpdateHelper.NOTIFY_UPDATE_POLICY); + } + else if(policyButton.equals(updateDownload.getModel())) { + UpdateHandler.setUpdatePolicy(UpdateHelper.DOWNLOAD_UPDATE_POLICY); + } + else if(policyButton.equals(updateDownloadRestart.getModel())) { + UpdateHandler.setUpdatePolicy(UpdateHelper.INSTALL_UPDATE_POLICY); + } + } + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel advancedPanel; private javax.swing.JToggleButton advancedUpdateConfig; private javax.swing.JPanel applyPanel; - private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JToggleButton cancel; private javax.swing.JToggleButton checkUpdates; private javax.swing.JScrollPane clientFrame; private javax.swing.JLabel clientTunnelLabel; private javax.swing.JLabel downloadSpeedLabel; + private javax.swing.JLabel downloadUsageLabel; private javax.swing.JTextField downloadgb; private javax.swing.JComboBox downloadkbps; private javax.swing.JTextField downloadspeed; - private javax.swing.JLabel jLabel3; - private javax.swing.JLabel jLabel4; - private javax.swing.JLabel jLabel5; - private javax.swing.JLabel jLabel6; - private javax.swing.JLabel jLabel7; + private javax.swing.JLabel gbDownloadLabel; + private javax.swing.JLabel gbUploadLabel; private javax.swing.JPanel networkPanel; private javax.swing.JToggleButton ok; private javax.swing.JScrollPane serverFrame; @@ -444,13 +662,16 @@ private void downloadgbKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_ private javax.swing.JPanel speedPanel; private javax.swing.JPanel tunnelPanel; private javax.swing.JLabel tunnelsExplanation; + private javax.swing.ButtonGroup updateButtonGroup; private javax.swing.JRadioButton updateDownload; private javax.swing.JRadioButton updateDownloadRestart; private javax.swing.JRadioButton updateInform; private javax.swing.JLabel updateMethod; private javax.swing.JToggleButton updateNow; private javax.swing.JPanel updatesPanel; + private javax.swing.JLabel uploadDownloadExplanation; private javax.swing.JLabel uploadSpeedLabel; + private javax.swing.JLabel uploadUsageLabel; private javax.swing.JTextField uploadgb; private javax.swing.JComboBox uploadkbps; private javax.swing.JTextField uploadspeed; @@ -458,4 +679,6 @@ private void downloadgbKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_ public static final int KILOBIT = 0; public static final int KILOBYTE = 1; + + private long newsLastFetched = 0; } diff --git a/apps/desktopgui/src/net/i2p/desktopgui/gui/Tray.java b/apps/desktopgui/src/net/i2p/desktopgui/gui/Tray.java index b20a850f6..02fb5b2df 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/gui/Tray.java +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/Tray.java @@ -151,7 +151,7 @@ public class Tray { public void actionPerformed(ActionEvent arg0) { RouterHandler.setStatus(RouterHandler.SHUTDOWN_GRACEFULLY); long shutdownTime = RouterHelper.getGracefulShutdownTimeRemaining(); - System.out.println(shutdownTime); + System.out.println("Shutdowntime remaining: " + shutdownTime); if(shutdownTime>0) { trayIcon.displayMessage("Shutting down...", "Shutdown time remaining: " + shutdownTime/1000 + " seconds." + System.getProperty("line.separator") + "Shutdown will not happen immediately, because we are still participating in the network.", TrayIcon.MessageType.INFO); @@ -170,7 +170,7 @@ public class Tray { popup.addSeparator(); config.add(speedConfig); - //config.add(generalConfig); + config.add(generalConfig); config.add(advancedConfig); popup.add(config); 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 e1c16a08c..c6af27a51 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/GeneralConfiguration.properties +++ b/apps/desktopgui/src/net/i2p/desktopgui/gui/resources/GeneralConfiguration.properties @@ -1,10 +1,5 @@ cancel.text=Cancel ok.text=OK -jLabel3.text=Monthly usage: -jLabel4.text=Monthly usage: -jLabel5.text=GB -jLabel6.text=GB -jLabel7.text=Explanation ... Form.title=General Configuration speedPanel.TabConstraints.tabTitle=Speed updatesPanel.TabConstraints.tabTitle=Updates @@ -27,3 +22,8 @@ advancedUpdateConfig.text=Advanced update configuration clientTunnelLabel.text=Client tunnels: serverTunnelLabel.text=Server tunnels: tunnelsExplanation.text=Tunnel explanation +uploadUsageLabel.text=Monthly usage: +downloadUsageLabel.text=Monthly usage: +gbUploadLabel.text=GB +gbDownloadLabel.text=GB +uploadDownloadExplanation.text=Explanation ... diff --git a/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/SpeedHelper.java b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/SpeedHelper.java index 5d2074de2..51b8b5a6f 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/SpeedHelper.java +++ b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/SpeedHelper.java @@ -36,4 +36,8 @@ public class SpeedHelper { public static String getInboundBandwidth() { return RouterHelper.getContext().router().getConfigSetting(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH); } + + public static String getOutboundBandwidth() { + return RouterHelper.getContext().router().getConfigSetting(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH); + } } diff --git a/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHandler.java b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHandler.java new file mode 100644 index 000000000..e913f5e51 --- /dev/null +++ b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHandler.java @@ -0,0 +1,19 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.desktopgui.router.configuration; + +import net.i2p.desktopgui.router.RouterHelper; + +/** + * + * @author mathias + */ +public class UpdateHandler { + public static void setUpdatePolicy(String policy) { + RouterHelper.getContext().router().setConfigSetting(UpdateHelper.PROP_UPDATE_POLICY, policy); + RouterHelper.getContext().router().saveConfig(); + } +} diff --git a/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java new file mode 100644 index 000000000..86db6f708 --- /dev/null +++ b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java @@ -0,0 +1,66 @@ +package net.i2p.desktopgui.router.configuration; + +import net.i2p.desktopgui.router.RouterHelper; + +/** + * + * @author mathias + */ +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 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"; + public static final String NOTIFY_UPDATE_POLICY = "notify"; + public static final String DOWNLOAD_UPDATE_POLICY = "download"; + public static final String INSTALL_UPDATE_POLICY = "install"; + public static final String DEFAULT_UPDATE_POLICY = DOWNLOAD_UPDATE_POLICY; + + public static final String PROP_SHOULD_PROXY = "router.updateThroughProxy"; + public static final String DEFAULT_SHOULD_PROXY = Boolean.TRUE.toString(); + public static final String PROP_PROXY_HOST = "router.updateProxyHost"; + public static final String DEFAULT_PROXY_HOST = "127.0.0.1"; + public static final String PROP_PROXY_PORT = "router.updateProxyPort"; + 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://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" ; + + public static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys"; + + public static String getNewsURL() { + String url = RouterHelper.getContext().getProperty(PROP_NEWS_URL); + if(url == null) { + return DEFAULT_NEWS_URL; + } + else { + return url; + } + } + + public static String getUpdatePolicy() { + String policy = null; + try { + policy = RouterHelper.getContext().getProperty(PROP_UPDATE_POLICY); + } + catch(Exception e) { + e.printStackTrace(); + } + System.out.println("Policy: " + policy); + if(policy == null) { + return DEFAULT_UPDATE_POLICY; + } + else { + return policy; + } + } +} diff --git a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java index 4e6bd5dc7..a44c90543 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java @@ -107,11 +107,26 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag break; } - msg = DataHelper.readLine(getClientSocket().socket().getInputStream()).trim(); + SocketChannel clientSocketChannel = getClientSocket() ; + if (clientSocketChannel == null) { + _log.info("Connection closed by client"); + break; + } + if (clientSocketChannel.socket() == null) { + _log.info("Connection closed by client"); + break; + } + java.io.InputStream is = clientSocketChannel.socket().getInputStream(); + if (is == null) { + _log.info("Connection closed by client"); + break; + } + msg = DataHelper.readLine(is); if (msg == null) { - _log.debug("Connection closed by client"); + _log.info("Connection closed by client (line read : null)"); break; } + msg = msg.trim(); if (_log.shouldLog(Log.DEBUG)) { _log.debug("New message received: [" + msg + "]"); diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java index 69f14430d..d7f119377 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java @@ -38,7 +38,7 @@ public class SAMv3DatagramSession extends SAMDatagramSession implements SAMv3Han * @throws I2PSessionException */ public SAMv3DatagramSession(String nick) - throws IOException, DataFormatException, I2PSessionException { + throws IOException, DataFormatException, I2PSessionException, SAMException { super(SAMv3Handler.sSessionsHash.get(nick).getDest(), SAMv3Handler.sSessionsHash.get(nick).getProps(), @@ -49,7 +49,7 @@ public class SAMv3DatagramSession extends SAMDatagramSession implements SAMv3Han this.server = SAMv3Handler.DatagramServer.getInstance() ; SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick); - if ( rec==null ) throw new InterruptedIOException() ; + if ( rec==null ) throw new SAMException("Record disappeared for nickname : \""+nick+"\"") ; this.handler = rec.getHandler(); diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java index 7a64d8441..80fa7cf63 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java @@ -564,7 +564,7 @@ public class SAMv3Handler extends SAMv1Handler _log.debug("I2P error when instantiating session", e); return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n"); } catch (SAMException e) { - _log.error("Unexpected SAM error", e); + _log.info("Funny SAM error", e); return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n"); } catch (IOException e) { _log.error("Unexpected IOException", e); diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java index 26d99fa01..699058613 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java @@ -197,7 +197,7 @@ public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handle SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick); - if ( rec==null ) throw new InterruptedIOException() ; + if ( rec==null || i2ps==null ) throw new InterruptedIOException() ; if (verbose) handler.notifyStreamIncomingConnection(i2ps.getPeerDestination()) ; diff --git a/build.xml b/build.xml index cd1b6c5b9..f7a7294e3 100644 --- a/build.xml +++ b/build.xml @@ -520,7 +520,7 @@ - + diff --git a/history.txt b/history.txt index 895f2bfc3..32bf7cb33 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,111 @@ +2009-05-29 sponge + * added big fat start/stop lock into BOB + * added zap command to shut down BOB... now we need a way to start it + after it stops. :-) + +2009-05-27 Mathiasdm + * Increase sendProcessingTime some more, add a property to configure. + Configure with 'router.defaultProcessingTimeThrottle'. + +2009-05-27 Mathiasdm + * Increased sendProcessingTime limits and added testSuccessTime + to avoid unwanted throttling + +2009-05-26 Mathiasdm + * Throttling extension by looking at sendProcessingTime + +2009-05-26 zzz + * Console: + - configlogging.jsp cleanup + - Flags tweak + * NetDb: + - Don't send our own hash in the don't-include list when exploring + - Remove any pending write when removing a RouterInfo + - Cleanup to use routerHash() + * Streaming: Hopefuly fix infinite loop in the SYN queue handler + +2009-05-25 zzz + * GeoIP: + - Save our own location in the config + - Check whole netDb at startup (last try didn't work) + * NTCP: + - Increase routerinfo send frequency to every 90m (was 9h) + - Don't send 3 floodfill infos at startup or with routerinfo + * Profile Organizer: Increase min fast peers based on + number of local destinations + * Timestamper: + - Use GeoIP to query a closer ntp source if available + - Lengthen query time if well-synced + - Cleanup + +2009-05-24 mkvore + * SAM: logging some exceptions at INFO level instead of ERROR + +2009-05-24 zzz + * Connection limits / throttle: + - Better limits when no inbound TCP + (limit inbound and outbound separately) + - Don't offer to SSU introduce when near connection limit + * Console: + - Move flags from icons/ to docs/icons + - peers.jsp cleanup + - Add readme_zh.html + * GeoIP: + - Check netDb SSU IP too + - Check whole netDb at startup + * NTCP: Log who is sending us big messages + * UPnP: Move logging from wrapper log to router log + +2009-05-23 Mathiasdm + * Router netDB: + - Added flags to the netDB page + +2009-05-22 Mathiasdm + * desktopgui: + - Updating works in general config + - Switched to Swingworker threads for improved responsiveness + +2009-05-21 zzz + * Router Watchdog: + - Log memory stats + - Dump threads on linux + - Restart after 20 minutes (give the dog his teeth back) + +2009-05-21 zzz + * DataStore: + - Adjust interface to have persistent and non-persistent methods, + to prepare for partial storage in RAM + * ExpireRoutersJob: + - Rewrite, not enabled yet + * I2Punnel: + - Increase eepsite default to 3+0 for new installs + * PersistentDataStore: + - Cleanup, simplify, and concurrentify + - Tweak stats + - Remove write limit + - Flush to disk on shutdown + - Don't write out what we just read in + * Router and console: + - Bundle geoIP files and flags in new installs, + spiff up tunnels.jsp and profiles.jsp. + Existing installs can get files with 'ant updaterWIthGeoIP' + or in the console docs bundle 'ant consoleDocs' + - Use flags for shitlist and peers.jsp too + - Tweak tunnels.jsp to show class letters + - Hide in-progress details on tunnels.jsp + - Add a little color to confignav + - Remove 'no skew' message + - More message tweaks if no wrapper + * TunnelManager: + - Remove now-unused isInUse() + * UPnP: + - Fix up port binding, add some logging on bind fails + - Force IPv4 only for binds + +2009-05-20 Mathiasdm + * General configuration enabled by default + * General configuration speed tab works completely + 2009-05-17 zzz * Merge i2p.i2p.zzz.upnp branch * Major changes: diff --git a/router/java/nbproject/project.xml b/router/java/nbproject/project.xml index 0b1c4e8ad..3947ad82a 100644 --- a/router/java/nbproject/project.xml +++ b/router/java/nbproject/project.xml @@ -10,6 +10,11 @@ i2p_router + + + . + UTF-8 + java @@ -22,11 +27,6 @@ test UTF-8 - - - . - UTF-8 - @@ -73,6 +73,7 @@ + diff --git a/router/java/src/net/i2p/router/RouterThrottleImpl.java b/router/java/src/net/i2p/router/RouterThrottleImpl.java index acab90a64..214753db2 100644 --- a/router/java/src/net/i2p/router/RouterThrottleImpl.java +++ b/router/java/src/net/i2p/router/RouterThrottleImpl.java @@ -33,6 +33,8 @@ class RouterThrottleImpl implements RouterThrottle { private static final String PROP_MAX_TUNNELS = "router.maxParticipatingTunnels"; private static final int DEFAULT_MAX_TUNNELS = 2000; private static final String PROP_DEFAULT_KBPS_THROTTLE = "router.defaultKBpsThrottle"; + private static final String PROP_MAX_PROCESSINGTIME = "router.defaultProcessingTimeThrottle"; + private static final int DEFAULT_MAX_PROCESSINGTIME = 1500; /** tunnel acceptance */ public static final int TUNNEL_ACCEPT = 0; @@ -96,19 +98,48 @@ class RouterThrottleImpl implements RouterThrottle { return TunnelHistory.TUNNEL_REJECT_BANDWIDTH; long lag = _context.jobQueue().getMaxLag(); -// reject here if lag too high??? + // reject here if lag too high??? + RateStat rs = _context.statManager().getRate("transport.sendProcessingTime"); Rate r = null; - if (rs != null) - r = rs.getRate(60*1000); - double processTime = (r != null ? r.getAverageValue() : 0); - if (processTime > 5000) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Refusing tunnel request with the job lag of " + lag - + "since the 1 minute message processing time is too slow (" + processTime + ")"); - _context.statManager().addRateData("router.throttleTunnelProcessingTime1m", (long)processTime, (long)processTime); - setTunnelStatus("Rejecting tunnels: High message delay"); - return TunnelHistory.TUNNEL_REJECT_TRANSIENT_OVERLOAD; + + //Reject tunnels if the time to process messages and send them is too large. Too much time implies congestion. + if(r != null) { + double totalSendProcessingTimeEvents = r.getCurrentEventCount() + r.getLastEventCount(); + double avgSendProcessingTime = 0; + double currentSendProcessingTime = 0; + double lastSendProcessingTime = 0; + + //Calculate times + if(r.getCurrentEventCount() > 0) { + currentSendProcessingTime = r.getCurrentTotalValue()/r.getCurrentEventCount(); + } + if(r.getLastEventCount() > 0) { + lastSendProcessingTime = r.getLastTotalValue()/r.getLastEventCount(); + } + if(totalSendProcessingTimeEvents > 0) { + avgSendProcessingTime = (r.getCurrentTotalValue() + r.getLastTotalValue())/totalSendProcessingTimeEvents; + } + else { + avgSendProcessingTime = r.getAverageValue(); + if(_log.shouldLog(Log.WARN)) { + _log.warn("No events occurred. Using 1 minute average to look at message delay."); + } + } + + int maxProcessingTime = _context.getProperty(PROP_MAX_PROCESSINGTIME, DEFAULT_MAX_PROCESSINGTIME); + + //Set throttling if necessary + if((avgSendProcessingTime > maxProcessingTime*0.9 + || currentSendProcessingTime > maxProcessingTime + || lastSendProcessingTime > maxProcessingTime)) { + if(_log.shouldLog(Log.WARN)) { + _log.warn("Refusing tunnel request due to sendProcessingTime of " + avgSendProcessingTime + + " ms over the last two minutes, which is too much."); + } + setTunnelStatus("Rejecting tunnels: congestion"); + return TunnelHistory.TUNNEL_REJECT_BANDWIDTH; + } } int numTunnels = _context.tunnelManager().getParticipatingCount(); diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index b74a3a021..f47a425fd 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 = 9; /** 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/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index aed932e5b..37503ecac 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -1067,6 +1067,11 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { else buf.append("Published: in ").append(DataHelper.formatDuration(0-age)).append("???
\n"); buf.append("Address(es): "); + String country = _context.commSystem().getCountry(info.getIdentity().getHash()); + if(country != null) { + buf.append(" \"").append(country.toUpperCase()).append("\"");"); + } for (Iterator iter = info.getAddresses().iterator(); iter.hasNext(); ) { RouterAddress addr = (RouterAddress)iter.next(); buf.append(addr.getTransportStyle()).append(": ");