diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index 4daa49069..0585c240f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -27,6 +27,7 @@ * not obligated to do so. If you do not wish to do so, delete this * exception statement from your version. */ + package net.i2p.i2ptunnel; import java.io.BufferedReader; @@ -288,8 +289,8 @@ public class I2PTunnel implements Logging, EventDispatcher { l.log("textserver "); l.log("genkeys []"); l.log("gentextkeys"); - l.log("client [,"); - l.log("httpclient "); + l.log("client [, []"); + l.log("httpclient [] []"); l.log("lookup "); l.log("quit"); l.log("close [forced] |all"); @@ -486,12 +487,16 @@ public class I2PTunnel implements Logging, EventDispatcher { * Also sets the event "openClientResult" = "error" or "ok" (before setting the value to "ok" it also * adds "Ready! Port #" to the logger as well). In addition, it will also set "clientLocalPort" = * Integer port number if the client is listening + * sharedClient parameter is a String "true" or "false" * - * @param args {portNumber, destinationBase64 or "file:filename"} + * @param args {portNumber, destinationBase64 or "file:filename"[, sharedClient]} * @param l logger to receive events and output */ public void runClient(String args[], Logging l) { - if (args.length == 2) { + boolean isShared = true; + if (args.length == 3) + isShared = Boolean.valueOf(args[2].trim()).booleanValue(); + if ( (args.length == 2) || (args.length == 3) ) { int portNum = -1; try { portNum = Integer.parseInt(args[0]); @@ -502,6 +507,7 @@ public class I2PTunnel implements Logging, EventDispatcher { return; } I2PTunnelTask task; + ownDest = !isShared; try { task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this); addtask(task); @@ -512,11 +518,12 @@ public class I2PTunnel implements Logging, EventDispatcher { notifyEvent("clientTaskId", new Integer(-1)); } } else { - l.log("client [,]|file:"); + l.log("client [,]|file:[ ]"); l.log(" creates a client that forwards port to the pubkey.\n" + " use 0 as port to get a free port assigned. If you specify\n" + " a comma delimited list of pubkeys, it will rotate among them\n" - + " randomlyl"); + + " randomlyl. sharedClient indicates if this client shares \n" + + " with other clients (true of false)"); notifyEvent("clientTaskId", new Integer(-1)); } } @@ -526,12 +533,13 @@ public class I2PTunnel implements Logging, EventDispatcher { * * Sets the event "httpclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error). * Also sets "httpclientStatus" = "ok" or "error" after the client tunnel has started. + * parameter sharedClient is a String, either "true" or "false" * - * @param args {portNumber and (optionally) proxy to be used for the WWW} + * @param args {portNumber[, sharedClient][, proxy to be used for the WWW]} * @param l logger to receive events and output */ public void runHttpClient(String args[], Logging l) { - if (args.length >= 1 && args.length <= 2) { + if (args.length >= 1 && args.length <= 3) { int port = -1; try { port = Integer.parseInt(args[0]); @@ -541,12 +549,32 @@ public class I2PTunnel implements Logging, EventDispatcher { notifyEvent("httpclientTaskId", new Integer(-1)); return; } - + String proxy = "squid.i2p"; - if (args.length == 2) { - proxy = args[1]; + boolean isShared = true; + if (args.length > 1) { + if ("true".equalsIgnoreCase(args[1].trim())) { + isShared = true; + if (args.length == 3) + proxy = args[2]; + } else if ("false".equalsIgnoreCase(args[1].trim())) { + _log.warn("args[1] == [" + args[1] + "] and rejected explicitly"); + isShared = false; + if (args.length == 3) + proxy = args[2]; + } else if (args.length == 3) { + isShared = false; // not "true" + proxy = args[2]; + _log.warn("args[1] == [" + args[1] + "] but rejected"); + } else { + // isShared not specified, default to true + isShared = true; + proxy = args[1]; + } } + I2PTunnelTask task; + ownDest = !isShared; try { task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this); addtask(task); @@ -557,8 +585,9 @@ public class I2PTunnel implements Logging, EventDispatcher { notifyEvent("httpclientTaskId", new Integer(-1)); } } else { - l.log("httpclient []"); + l.log("httpclient [] []"); l.log(" creates a client that distributes HTTP requests."); + l.log(" (optional) indicates if this client shares tunnels with other clients (true of false)"); l.log(" (optional) indicates a proxy server to be used"); l.log(" when trying to access an address out of the .i2p domain"); l.log(" (the default proxy is squid.i2p)."); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index ec86e18d8..3d216e06f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -153,10 +153,11 @@ public class TunnelController implements Logging { setListenOn(); String listenPort = getListenPort(); String proxyList = getProxyList(); + String sharedClient = getSharedClient(); if (proxyList == null) - _tunnel.runHttpClient(new String[] { listenPort }, this); + _tunnel.runHttpClient(new String[] { listenPort, sharedClient }, this); else - _tunnel.runHttpClient(new String[] { listenPort, proxyList }, this); + _tunnel.runHttpClient(new String[] { listenPort, sharedClient, proxyList }, this); acquire(); _running = true; } @@ -199,7 +200,8 @@ public class TunnelController implements Logging { setListenOn(); String listenPort = getListenPort(); String dest = getTargetDestination(); - _tunnel.runClient(new String[] { listenPort, dest }, this); + String sharedClient = getSharedClient(); + _tunnel.runClient(new String[] { listenPort, dest, sharedClient }, this); acquire(); _running = true; } @@ -331,6 +333,7 @@ public class TunnelController implements Logging { public String getListenPort() { return _config.getProperty("listenPort"); } public String getTargetDestination() { return _config.getProperty("targetDestination"); } public String getProxyList() { return _config.getProperty("proxyList"); } + public String getSharedClient() { return _config.getProperty("sharedClient", "true"); } public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); } public String getMyDestination() { if (_tunnel != null) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelManager.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelManager.java index 4fe805383..3b76c5e32 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelManager.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelManager.java @@ -1,6 +1,7 @@ /* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java) * (c) 2003 - 2004 mihi */ + package net.i2p.i2ptunnel; import java.io.IOException; @@ -55,7 +56,7 @@ import net.i2p.util.Log; * Sets the ip address clients will listen on. By default this is the * localhost (127.0.0.1) * ------------------------------------------------- - * openclient <listenPort> <peer>\n + * openclient <listenPort> <peer>[ <sharedClient>]\n * -- * ok [<jobId>]\n * or @@ -70,8 +71,10 @@ import net.i2p.util.Log; * specified as 'file:<filename>' or the name of a destination listed in * hosts.txt. The <jobId> returned together with "ok" and <listenport> can * later be used as argument for the "close" command. + * <sharedClient> indicates if this httpclient shares tunnels with other + * clients or not (just use 'true' and 'false' * ------------------------------------------------- - * openhttpclient <listenPort> [<proxy>]\n + * openhttpclient <listenPort> [<sharedClient>] [<proxy>]\n * -- * ok [<jobId>]\n * or @@ -90,6 +93,8 @@ import net.i2p.util.Log; * hosts.txt. The <jobId> returned together with "ok" and * <listenport> can later be used as argument for the "close" * command. + * <sharedClient> indicates if this httpclient shares tunnels with other + * clients or not (just use 'true' and 'false' * ------------------------------------------------- * opensockstunnel <listenPort>\n * -- @@ -145,6 +150,11 @@ import net.i2p.util.Log; * description depends on the type of job. * ------------------------------------------------- * + * + * + * @deprecated this isn't run by default, and no one seems to use it, and has + * lots of things to maintain. so, at some point this may dissapear + * unless someone pipes up ;) */ public class TunnelManager implements Runnable { private final static Log _log = new Log(TunnelManager.class); @@ -322,9 +332,9 @@ public class TunnelManager implements Runnable { buf.ignoreFurtherActions(); } - public void processOpenClient(int listenPort, String peer, OutputStream out) throws IOException { + public void processOpenClient(int listenPort, String peer, String sharedClient, OutputStream out) throws IOException { BufferLogger buf = new BufferLogger(); - _tunnel.runCommand("client " + listenPort + " " + peer, buf); + _tunnel.runCommand("client " + listenPort + " " + peer + " " + sharedClient, buf); Integer taskId = (Integer) _tunnel.waitEventValue("clientTaskId"); if (taskId.intValue() < 0) { out.write("error\n".getBytes()); @@ -348,9 +358,9 @@ public class TunnelManager implements Runnable { buf.ignoreFurtherActions(); } - public void processOpenHTTPClient(int listenPort, String proxy, OutputStream out) throws IOException { + public void processOpenHTTPClient(int listenPort, String sharedClient, String proxy, OutputStream out) throws IOException { BufferLogger buf = new BufferLogger(); - _tunnel.runCommand("httpclient " + listenPort + " " + proxy, buf); + _tunnel.runCommand("httpclient " + listenPort + " " + sharedClient + " " + proxy, buf); Integer taskId = (Integer) _tunnel.waitEventValue("httpclientTaskId"); if (taskId.intValue() < 0) { out.write("error\n".getBytes()); @@ -432,4 +442,4 @@ public class TunnelManager implements Runnable { out.write(command.getBytes()); out.write("\n".getBytes()); } -} \ No newline at end of file +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelManagerClientRunner.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelManagerClientRunner.java index c62928eb6..6bd9bb730 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelManagerClientRunner.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelManagerClientRunner.java @@ -1,6 +1,7 @@ /* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java) * (c) 2003 - 2004 mihi */ + package net.i2p.i2ptunnel; import java.io.BufferedReader; @@ -102,45 +103,53 @@ class TunnelManagerClientRunner implements Runnable { } else if ("openclient".equalsIgnoreCase(cmd)) { int listenPort = 0; String peer = null; - if (!tok.hasMoreTokens()) { - _mgr.error("Usage: openclient ", out); + String sharedClient = null; + int numTokens = tok.countTokens(); + if (numTokens < 2 || numTokens > 3) { + _mgr.error("Usage: openclient ", out); return; } try { - String portStr = tok.nextToken(); - listenPort = Integer.parseInt(portStr); + listenPort = Integer.parseInt(tok.nextToken()); + peer = tok.nextToken(); + if (tok.hasMoreTokens()) + sharedClient = tok.nextToken(); + else + sharedClient = "true"; + _mgr.processOpenClient(listenPort, peer, sharedClient, out); } catch (NumberFormatException nfe) { _mgr.error("Bad listen port", out); return; } - if (!tok.hasMoreTokens()) { - _mgr.error("Usage: openclient ", out); - return; - } - peer = tok.nextToken(); - _mgr.processOpenClient(listenPort, peer, out); } else if ("openhttpclient".equalsIgnoreCase(cmd)) { int listenPort = 0; String proxy = "squid.i2p"; - if (!tok.hasMoreTokens()) { - _mgr.error("Usage: openhttpclient []", out); + String sharedClient = "true"; + int numTokens = tok.countTokens(); + if (numTokens < 1 || numTokens > 3) { + _mgr.error("Usage: openhttpclient [] []", out); return; } try { - String portStr = tok.nextToken(); - listenPort = Integer.parseInt(portStr); + listenPort = Integer.parseInt(tok.nextToken()); + if (tok.hasMoreTokens()) { + String val = tok.nextToken(); + if (tok.hasMoreTokens()) { + sharedClient = val; + proxy = tok.nextToken(); + } else { + if ( ("true".equals(val)) || ("false".equals(val)) ) { + sharedClient = val; + } else { + proxy = val; + } + } + } + _mgr.processOpenHTTPClient(listenPort, sharedClient, proxy, out); } catch (NumberFormatException nfe) { _mgr.error("Bad listen port", out); return; } - if (tok.hasMoreTokens()) { - proxy = tok.nextToken(); - } - if (tok.hasMoreTokens()) { - _mgr.error("Usage: openclient []", out); - return; - } - _mgr.processOpenHTTPClient(listenPort, proxy, out); } else if ("opensockstunnel".equalsIgnoreCase(cmd)) { int listenPort = 0; if (!tok.hasMoreTokens()) { @@ -191,4 +200,4 @@ class TunnelManagerClientRunner implements Runnable { } } } -} \ No newline at end of file +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java index 9e87082e7..3e8ad4ea1 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -73,6 +73,14 @@ public class EditBean extends IndexBean { return false; } + public boolean isSharedClient(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return "true".equalsIgnoreCase(tun.getSharedClient()); + else + return true; + } + public boolean shouldDelay(int tunnel) { TunnelController tun = getController(tunnel); if (tun != null) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index cff2cd670..cadcd544a 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -52,6 +52,7 @@ public class IndexBean { private String _privKeyFile; private String _profile; private boolean _startOnLoad; + private boolean _sharedClient; private boolean _privKeyGenerate; private boolean _removeConfirmed; @@ -204,7 +205,8 @@ public class IndexBean { for (int i = 0; i < controllers.size(); i++) { TunnelController c = (TunnelController)controllers.get(i); if (c == cur) continue; - if ("httpclient".equals(c.getType()) || "client".equals(c.getType())) { + //only change when they really are declared of beeing a sharedClient + if (("httpclient".equals(c.getType()) || "client".equals(c.getType())) && "true".equalsIgnoreCase(c.getSharedClient())) { Properties cOpt = c.getConfig(""); if (_tunnelCount != null) { cOpt.setProperty("option.inbound.quantity", _tunnelCount); @@ -343,6 +345,14 @@ public class IndexBean { return ""; } + public String getSharedClient(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getSharedClient(); + else + return ""; + } + public String getClientDestination(int tunnel) { TunnelController tun = getController(tunnel); if (tun == null) return ""; @@ -469,6 +479,9 @@ public class IndexBean { public void setStartOnLoad(String moo) { _startOnLoad = true; } + public void setSharedClient(String moo) { + _sharedClient=true; + } public void setConnectDelay(String moo) { _connectDelay = true; } @@ -496,8 +509,14 @@ public class IndexBean { if (_proxyList != null) config.setProperty("proxyList", _proxyList); - config.setProperty("option.inbound.nickname", CLIENT_NICKNAME); - config.setProperty("option.outbound.nickname", CLIENT_NICKNAME); + config.setProperty("option.inbound.nickname", CLIENT_NICKNAME); + config.setProperty("option.outbound.nickname", CLIENT_NICKNAME); + if (_name != null && !_sharedClient) { + config.setProperty("option.inbound.nickname", _name); + config.setProperty("option.outbound.nickname", _name); + } + + config.setProperty("sharedClient", _sharedClient + ""); } else if ("client".equals(_type)) { if (_port != null) config.setProperty("listenPort", _port); @@ -510,6 +529,11 @@ public class IndexBean { config.setProperty("option.inbound.nickname", CLIENT_NICKNAME); config.setProperty("option.outbound.nickname", CLIENT_NICKNAME); + if (_name != null && !_sharedClient) { + config.setProperty("option.inbound.nickname", _name); + config.setProperty("option.outbound.nickname", _name); + } + config.setProperty("sharedClient", _sharedClient + ""); } else if ("server".equals(_type)) { if (_targetHost != null) config.setProperty("targetHost", _targetHost); @@ -567,7 +591,7 @@ public class IndexBean { } config.setProperty("startOnLoad", _startOnLoad + ""); - + if (_tunnelCount != null) { config.setProperty("option.inbound.quantity", _tunnelCount); config.setProperty("option.outbound.quantity", _tunnelCount); @@ -581,7 +605,7 @@ public class IndexBean { else config.setProperty("option.i2p.streaming.connectDelay", "0"); if (_name != null) { - if ( (!"client".equals(_type)) && (!"httpclient".equals(_type)) ) { + if ( ((!"client".equals(_type)) && (!"httpclient".equals(_type))) || (!_sharedClient) ) { config.setProperty("option.inbound.nickname", _name); config.setProperty("option.outbound.nickname", _name); } else { diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 882a69417..9f22cbcfa 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -161,10 +161,23 @@ if (curTunnel >= 0) { + +Shared Client + + +<% if (editBean.isSharedClient(curTunnel)) { %> + +<% } else { %> + +<% } %> +(Share tunnels with other clients and httpclients? Change requires restart of client proxy) + + +
Advanced networking options
-(Those are shared between ALL your Client proxies!)
+(NOTE: when this client proxy is configured to share tunnels, then these options are for all the shared proxy clients!) diff --git a/history.txt b/history.txt index c49f4563e..34c75003e 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,15 @@ -$Id: history.txt,v 1.193 2005/04/16 19:59:51 jrandom Exp $ +$Id: history.txt,v 1.194 2005/04/17 18:23:21 jrandom Exp $ + +2005-04-17 sirup + * Added the possibility for i2ptunnel client and httpclient instances to + have their own i2p session (and hence, destination and tunnels). By + default, tunnels are shared, but that can be changed on the web + interface or with the sharedClient config option in i2ptunnel.config. + +2005-04-17 jrandom + * Marked the net.i2p.i2ptunnel.TunnelManager as deprecated. Anyone use + this? If not, I want to drop it (lots of tiny details with lots of + duplicated semantics). 2005-04-17 zzz * Added new user-editable eepproxy error page templates. diff --git a/installer/resources/i2ptunnel.config b/installer/resources/i2ptunnel.config index 2a075455a..45edb0861 100644 --- a/installer/resources/i2ptunnel.config +++ b/installer/resources/i2ptunnel.config @@ -2,13 +2,14 @@ tunnel.0.name=eepProxy tunnel.0.description=HTTP proxy for browsing eepsites and the web tunnel.0.type=httpclient +tunnel.0.sharedClient=true tunnel.0.interface=127.0.0.1 tunnel.0.listenPort=4444 tunnel.0.proxyList=squid.i2p,www1.squid.i2p tunnel.0.i2cpHost=127.0.0.1 tunnel.0.i2cpPort=7654 -tunnel.0.option.inbound.nickname=eepProxy -tunnel.0.option.outbound.nickname=eepProxy +tunnel.0.option.inbound.nickname=shared clients +tunnel.0.option.outbound.nickname=shared clients tunnel.0.option.i2p.streaming.connectDelay=1000 tunnel.0.startOnLoad=true @@ -16,13 +17,14 @@ tunnel.0.startOnLoad=true tunnel.1.name=ircProxy tunnel.1.description=IRC proxy to access the anonymous irc net tunnel.1.type=client +tunnel.1.sharedClient=true tunnel.1.interface=127.0.0.1 tunnel.1.listenPort=6668 tunnel.1.targetDestination=irc.duck.i2p,irc.baffled.i2p,irc.postman.i2p tunnel.1.i2cpHost=127.0.0.1 tunnel.1.i2cpPort=7654 -tunnel.1.option.inbound.nickname=ircProxy -tunnel.1.option.outbound.nickname=ircProxy +tunnel.1.option.inbound.nickname=shared clients +tunnel.1.option.outbound.nickname=shared clients tunnel.1.option.i2p.streaming.connectDelay=1000 tunnel.1.option.i2p.maxWindowSize=1 tunnel.1.startOnLoad=true @@ -31,13 +33,14 @@ tunnel.1.startOnLoad=true tunnel.2.name=cvs.i2p tunnel.2.description=I2P CVS pserver tunnel.2.type=client +tunnel.2.sharedClient=true tunnel.2.interface=127.0.0.1 tunnel.2.listenPort=2401 tunnel.2.targetDestination=cvs.i2p tunnel.2.i2cpHost=127.0.0.1 tunnel.2.i2cpPort=7654 -tunnel.2.option.inbound.nickname=cvsProxy -tunnel.2.option.outbound.nickname=cvsProxy +tunnel.2.option.inbound.nickname=shared clients +tunnel.2.option.outbound.nickname=shared clients tunnel.2.startOnLoad=false # local eepserver @@ -61,12 +64,13 @@ tunnel.4.i2cpPort=7654 tunnel.4.interface=127.0.0.1 tunnel.4.listenPort=7659 tunnel.4.name=smtp.postman.i2p -tunnel.4.option.inbound.nickname=smtp -tunnel.4.option.outbound.nickname=smtp +tunnel.4.option.inbound.nickname=shared clients +tunnel.4.option.outbound.nickname=shared clients tunnel.4.option.i2p.streaming.connectDelay=1000 tunnel.4.startOnLoad=true tunnel.4.targetDestination=smtp.postman.i2p tunnel.4.type=client +tunnel.4.sharedClient=true # postman's POP3 server - see www.postman.i2p tunnel.5.name=pop3.postman.i2p @@ -75,8 +79,8 @@ tunnel.5.i2cpHost=127.0.0.1 tunnel.5.i2cpPort=7654 tunnel.5.interface=127.0.0.1 tunnel.5.listenPort=7660 -tunnel.5.option.inbound.nickname=pop3 -tunnel.5.option.outbound.nickname=pop3 +tunnel.5.option.inbound.nickname=shared clients +tunnel.5.option.outbound.nickname=shared clients tunnel.5.option.i2p.streaming.connectDelay=1000 tunnel.5.startOnLoad=true tunnel.5.targetDestination=pop.postman.i2p diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 352ae7679..6fdf15706 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.185 $ $Date: 2005/04/16 19:59:51 $"; + public final static String ID = "$Revision: 1.186 $ $Date: 2005/04/17 18:23:20 $"; public final static String VERSION = "0.5.0.6"; - public final static long BUILD = 3; + public final static long BUILD = 4; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION); System.out.println("Router ID: " + RouterVersion.ID);