propagate from branch 'i2p.i2p.zzz.test4' (head 56ba5c9f8d0779f91259df05b7be0826fe08cd84)

to branch 'i2p.i2p' (head 0ba2cc80363f5c7086bce7a43f43a9b095ed2d9e)
This commit is contained in:
zzz
2010-12-27 16:21:09 +00:00
120 changed files with 4192 additions and 2231 deletions

View File

@ -9,6 +9,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import net.i2p.router.client.ClientManagerFacadeImpl;
import net.i2p.router.startup.ClientAppConfig;
import net.i2p.router.startup.LoadClientAppsJob;
@ -35,6 +36,10 @@ public class ConfigClientsHandler extends FormHandler {
saveClientChanges();
return;
}
if (_action.equals(_("Save Interface Configuration"))) {
saveInterfaceChanges();
return;
}
if (_action.equals(_("Save WebApp Configuration"))) {
saveWebAppChanges();
return;
@ -193,7 +198,7 @@ public class ConfigClientsHandler extends FormHandler {
String[] arr = (String[]) _settings.get(key);
if (arr == null)
return null;
return arr[0];
return arr[0].trim();
}
private void startClient(int i) {
@ -337,4 +342,37 @@ public class ConfigClientsHandler extends FormHandler {
_log.error("Error starting plugin " + app, e);
}
}
/**
* Handle interface form
* @since 0.8.3
*/
private void saveInterfaceChanges() {
String port = getJettyString("port");
if (port != null)
_context.router().setConfigSetting(ClientManagerFacadeImpl.PROP_CLIENT_PORT, port);
String intfc = getJettyString("interface");
if (intfc != null)
_context.router().setConfigSetting(ClientManagerFacadeImpl.PROP_CLIENT_HOST, intfc);
String user = getJettyString("user");
if (user != null)
_context.router().setConfigSetting(ConfigClientsHelper.PROP_USER, user);
String pw = getJettyString("pw");
if (pw != null)
_context.router().setConfigSetting(ConfigClientsHelper.PROP_PW, pw);
String mode = getJettyString("mode");
boolean disabled = "0".equals(mode);
boolean ssl = "2".equals(mode);
_context.router().setConfigSetting(ConfigClientsHelper.PROP_DISABLE_EXTERNAL,
Boolean.toString(disabled));
_context.router().setConfigSetting(ConfigClientsHelper.PROP_ENABLE_SSL,
Boolean.toString(ssl));
_context.router().setConfigSetting(ConfigClientsHelper.PROP_AUTH,
Boolean.toString((_settings.get("auth") != null)));
boolean all = "0.0.0.0".equals(intfc) || "0:0:0:0:0:0:0:0".equals(intfc) ||
"::".equals(intfc);
_context.router().setConfigSetting(ConfigClientsHelper.BIND_ALL_INTERFACES, Boolean.toString(all));
_context.router().saveConfig();
addFormNotice(_("Interface configuration saved successfully - restart required to take effect."));
}
}

View File

@ -1,6 +1,7 @@
package net.i2p.router.web;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@ -8,13 +9,76 @@ import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import net.i2p.router.client.ClientManagerFacadeImpl;
import net.i2p.router.startup.ClientAppConfig;
import net.i2p.util.Addresses;
public class ConfigClientsHelper extends HelperBase {
private String _edit;
/** from ClientListenerRunner */
public static final String BIND_ALL_INTERFACES = "i2cp.tcp.bindAllInterfaces";
/** from ClientManager */
public static final String PROP_DISABLE_EXTERNAL = "i2cp.disableInterface";
public static final String PROP_ENABLE_SSL = "i2cp.SSL";
/** from ClientMessageEventListener */
public static final String PROP_AUTH = "i2cp.auth";
public static final String PROP_USER = "i2cp.username";
public static final String PROP_PW = "i2cp.password";
public ConfigClientsHelper() {}
/** @since 0.8.3 */
public String getPort() {
return _context.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_PORT,
Integer.toString(ClientManagerFacadeImpl.DEFAULT_PORT));
}
/** @since 0.8.3 */
public String getUser() {
return _context.getProperty(PROP_USER, "");
}
/** @since 0.8.3 */
public String getPw() {
return _context.getProperty(PROP_PW, "");
}
/** @since 0.8.3 */
public String i2cpModeChecked(int mode) {
boolean disabled = _context.getBooleanProperty(PROP_DISABLE_EXTERNAL);
boolean ssl = _context.getBooleanProperty(PROP_ENABLE_SSL);
if ((mode == 0 && disabled) ||
(mode == 1 && (!disabled) && (!ssl)) ||
(mode == 2 && (!disabled) && ssl))
return "checked=\"true\"";
return "";
}
/** @since 0.8.3 */
public String getAuth() {
boolean enabled = _context.getBooleanProperty(PROP_AUTH);
if (enabled)
return "checked=\"true\"";
return "";
}
/** @since 0.8.3 */
public String[] intfcAddresses() {
ArrayList<String> al = new ArrayList(Addresses.getAllAddresses());
return al.toArray(new String[al.size()]);
}
/** @since 0.8.3 */
public boolean isIFSelected(String addr) {
boolean bindAll = _context.getBooleanProperty(BIND_ALL_INTERFACES);
if (bindAll && addr.equals("0.0.0.0") || addr.equals("::"))
return true;
String host = _context.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_HOST,
ClientManagerFacadeImpl.DEFAULT_HOST);
return (host.equals(addr));
}
public void setEdit(String edit) {
if (edit == null)
return;
@ -92,9 +156,9 @@ public class ConfigClientsHelper extends HelperBase {
continue;
StringBuilder desc = new StringBuilder(256);
desc.append("<table border=\"0\">")
.append("<tr><td><b>").append(_("Version")).append("<td>").append(stripHTML(appProps, "version"))
.append("<tr><td><b>").append(_("Version")).append("</b></td><td>").append(stripHTML(appProps, "version"))
.append("<tr><td><b>")
.append(_("Signed by")).append("<td>");
.append(_("Signed by")).append("</b></td><td>");
String s = stripHTML(appProps, "signer");
if (s != null) {
if (s.indexOf("@") > 0)
@ -111,13 +175,13 @@ public class ConfigClientsHelper extends HelperBase {
if (ms > 0) {
String date = (new SimpleDateFormat("yyyy-MM-dd HH:mm")).format(new Date(ms));
desc.append("<tr><td><b>")
.append(_("Date")).append("<td>").append(date);
.append(_("Date")).append("</b></td><td>").append(date);
}
}
s = stripHTML(appProps, "author");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("Author")).append("<td>");
.append(_("Author")).append("</b></td><td>");
if (s.indexOf("@") > 0)
desc.append("<a href=\"mailto:").append(s).append("\">").append(s).append("</a>");
else
@ -128,12 +192,12 @@ public class ConfigClientsHelper extends HelperBase {
s = stripHTML(appProps, "description");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("Description")).append("<td>").append(s);
.append(_("Description")).append("</b></td><td>").append(s);
}
s = stripHTML(appProps, "license");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("License")).append("<td>").append(s);
.append(_("License")).append("</b></td><td>").append(s);
}
s = stripHTML(appProps, "websiteURL");
if (s != null) {

View File

@ -1,14 +1,16 @@
package net.i2p.router.web;
import java.util.ArrayList;
import net.i2p.data.DataHelper;
import net.i2p.data.RouterAddress;
import net.i2p.router.CommSystemFacade;
import net.i2p.router.Router;
import net.i2p.router.transport.Addresses;
import net.i2p.router.transport.TransportManager;
import net.i2p.router.transport.udp.UDPAddress;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.time.Timestamper;
import net.i2p.util.Addresses;
public class ConfigNetHelper extends HelperBase {
public ConfigNetHelper() {}
@ -147,7 +149,8 @@ public class ConfigNetHelper extends HelperBase {
}
public String[] getAddresses() {
return Addresses.getAddresses();
ArrayList<String> al = new ArrayList(Addresses.getAddresses());
return al.toArray(new String[al.size()]);
}
public String getInboundRate() {

View File

@ -139,6 +139,6 @@ public class ConfigStatsHelper extends HelperBase {
public boolean getCurrentCanBeGraphed() { return _currentCanBeGraphed; }
public String getExplicitFilter() { return _filter; }
public boolean getIsFull() {
return _context.getBooleanPropertyDefaultTrue(StatManager.PROP_STAT_FULL);
return _context.getBooleanProperty(StatManager.PROP_STAT_FULL);
}
}

View File

@ -3,6 +3,7 @@ package net.i2p.router.web;
import net.i2p.I2PAppContext;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.data.DataHelper;
import net.i2p.util.FileUtil;
/**
*
@ -61,14 +62,10 @@ public class ConfigUpdateHandler extends FormHandler {
public static final String DEFAULT_UPDATE_URL;
static {
String foo;
try {
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
foo = PACK200_URLS;
} catch (ClassNotFoundException cnfe) {
foo = NO_PACK200_URLS;
}
DEFAULT_UPDATE_URL = foo;
if (FileUtil.isPack200Supported())
DEFAULT_UPDATE_URL = PACK200_URLS;
else
DEFAULT_UPDATE_URL = NO_PACK200_URLS;
}
public static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";

View File

@ -59,7 +59,13 @@ public class GraphHelper extends FormHandler {
try { _width = Math.min(Integer.parseInt(str), MAX_X); } catch (NumberFormatException nfe) {}
}
public void setRefreshDelay(String str) {
try { _refreshDelaySeconds = Math.max(Integer.parseInt(str), MIN_REFRESH); } catch (NumberFormatException nfe) {}
try {
int rds = Integer.parseInt(str);
if (rds > 0)
_refreshDelaySeconds = Math.max(rds, MIN_REFRESH);
else
_refreshDelaySeconds = -1;
} catch (NumberFormatException nfe) {}
}
public String getImages() {
@ -83,7 +89,7 @@ public class GraphHelper extends FormHandler {
+ "&amp;periodCount=" + (3 * _periodCount )
+ "&amp;width=" + (3 * _width)
+ "&amp;height=" + (3 * _height)
+ "\" / target=\"_blank\">");
+ "\" target=\"_blank\">");
String title = _("Combined bandwidth graph");
_out.write("<img class=\"statimage\" width=\""
+ (_width + 83) + "\" height=\"" + (_height + 92)
@ -129,6 +135,8 @@ public class GraphHelper extends FormHandler {
return "";
}
private static final int[] times = { 60, 2*60, 5*60, 10*60, 30*60, 60*60, -1 };
public String getForm() {
String prev = System.getProperty("net.i2p.router.web.GraphHelper.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.GraphHelper.noncePrev", prev);
@ -145,8 +153,22 @@ public class GraphHelper extends FormHandler {
_out.write(_("Image sizes") + ": " + _("width") + ": <input size=\"4\" type=\"text\" name=\"width\" value=\"" + _width
+ "\"> " + _("pixels") + ", " + _("height") + ": <input size=\"4\" type=\"text\" name=\"height\" value=\"" + _height
+ "\"> " + _("pixels") + "<br>\n");
_out.write(_("Refresh delay") + ": <select name=\"refreshDelay\"><option value=\"60\">1 " + _("minute") + "</option><option value=\"120\">2 " + _("minutes") + "</option><option value=\"300\">5 " + _("minutes") + "</option><option value=\"600\">10 " + _("minutes") + "</option><option value=\"1800\">30 " + _("minutes") + "</option><option value=\"3600\">1 " + _("hour") + "</option><option value=\"-1\">" + _("Never") + "</option></select><br>\n");
_out.write("<hr><div class=\"formaction\"><input type=\"submit\" value=\"" + _("Redraw") + "\"></div></form>");
_out.write(_("Refresh delay") + ": <select name=\"refreshDelay\">");
for (int i = 0; i < times.length; i++) {
_out.write("<option value=\"");
_out.write(Integer.toString(times[i]));
_out.write("\"");
if (times[i] == _refreshDelaySeconds)
_out.write(" selected=\"true\"");
_out.write(">");
if (times[i] > 0)
_out.write(DataHelper.formatDuration2(times[i] * 1000));
else
_out.write(_("Never"));
_out.write("</option>\n");
}
_out.write("</select><br>\n" +
"<hr><div class=\"formaction\"><input type=\"submit\" value=\"" + _("Redraw") + "\"></div></form>");
} catch (IOException ioe) {
ioe.printStackTrace();
}

View File

@ -53,7 +53,8 @@ public class LocaleWebAppHandler extends WebApplicationHandler
// home page
pathInContext = "/index.jsp";
} else if (pathInContext.indexOf("/", 1) < 0 &&
!pathInContext.endsWith(".jsp")) {
(!pathInContext.endsWith(".jsp")) &&
(!pathInContext.endsWith(".txt"))) {
// add .jsp to pages at top level
pathInContext += ".jsp";
}

View File

@ -4,37 +4,53 @@ import java.util.ArrayList;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.security.KeyStore;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.apps.systray.SysTray;
import net.i2p.data.Base32;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.ShellCommand;
import org.mortbay.http.DigestAuthenticator;
import org.mortbay.http.HashUserRealm;
import org.mortbay.http.SecurityConstraint;
import org.mortbay.http.SslListener;
import org.mortbay.http.handler.SecurityHandler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.WebApplicationContext;
import org.mortbay.jetty.servlet.WebApplicationHandler;
import org.mortbay.util.InetAddrPort;
public class RouterConsoleRunner {
private Server _server;
private String _listenPort = "7657";
private String _listenHost = "127.0.0.1";
private String _webAppsDir = "./webapps/";
private String _listenPort;
private String _listenHost;
private String _sslListenPort;
private String _sslListenHost;
private String _webAppsDir;
private static final String PROP_WEBAPP_CONFIG_FILENAME = "router.webappsConfigFile";
private static final String DEFAULT_WEBAPP_CONFIG_FILENAME = "webapps.config";
private static final DigestAuthenticator authenticator = new DigestAuthenticator();
public static final String ROUTERCONSOLE = "routerconsole";
public static final String PREFIX = "webapps.";
public static final String ENABLED = ".startOnLoad";
private static final String PROP_KEYSTORE_PASSWORD = "routerconsole.keystorePassword";
private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";
private static final String PROP_KEY_PASSWORD = "routerconsole.keyPassword";
private static final String DEFAULT_LISTEN_PORT = "7657";
private static final String DEFAULT_LISTEN_HOST = "127.0.0.1";
private static final String DEFAULT_WEBAPPS_DIR = "./webapps/";
private static final String USAGE = "Bad RouterConsoleRunner arguments, check clientApp.0.args in your clients.config file! " +
"Usage: [[port host[,host]] [-s sslPort [host[,host]]] [webAppsDir]]";
static {
System.setProperty("org.mortbay.http.Version.paranoid", "true");
@ -42,6 +58,27 @@ public class RouterConsoleRunner {
}
/**
* <pre>
* non-SSL:
* RouterConsoleRunner
* RouterConsoleRunner 7657
* RouterConsoleRunner 7657 127.0.0.1
* RouterConsoleRunner 7657 127.0.0.1,::1
* RouterConsoleRunner 7657 127.0.0.1,::1 ./webapps/
*
* SSL:
* RouterConsoleRunner -s 7657
* RouterConsoleRunner -s 7657 127.0.0.1
* RouterConsoleRunner -s 7657 127.0.0.1,::1
* RouterConsoleRunner -s 7657 127.0.0.1,::1 ./webapps/
*
* If using both, non-SSL must be first:
* RouterConsoleRunner 7657 127.0.0.1 -s 7667
* RouterConsoleRunner 7657 127.0.0.1 -s 7667 127.0.0.1
* RouterConsoleRunner 7657 127.0.0.1,::1 -s 7667 127.0.0.1,::1
* RouterConsoleRunner 7657 127.0.0.1,::1 -s 7667 127.0.0.1,::1 ./webapps/
* </pre>
*
* @param args second arg may be a comma-separated list of bind addresses,
* for example ::1,127.0.0.1
* On XP, the other order (127.0.0.1,::1) fails the IPV6 bind,
@ -50,10 +87,40 @@ public class RouterConsoleRunner {
* So the wise choice is ::1,127.0.0.1
*/
public RouterConsoleRunner(String args[]) {
if (args.length == 3) {
_listenPort = args[0].trim();
_listenHost = args[1].trim();
_webAppsDir = args[2].trim();
if (args.length == 0) {
// _listenHost and _webAppsDir are defaulted below
_listenPort = DEFAULT_LISTEN_PORT;
} else {
boolean ssl = false;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-s"))
ssl = true;
else if ((!ssl) && _listenPort == null)
_listenPort = args[i];
else if ((!ssl) && _listenHost == null)
_listenHost = args[i];
else if (ssl && _sslListenPort == null)
_sslListenPort = args[i];
else if (ssl && _sslListenHost == null)
_sslListenHost = args[i];
else if (_webAppsDir == null)
_webAppsDir = args[i];
else {
System.err.println(USAGE);
throw new IllegalArgumentException(USAGE);
}
}
}
if (_listenHost == null)
_listenHost = DEFAULT_LISTEN_HOST;
if (_sslListenHost == null)
_sslListenHost = _listenHost;
if (_webAppsDir == null)
_webAppsDir = DEFAULT_WEBAPPS_DIR;
// _listenPort and _sslListenPort are not defaulted, if one or the other is null, do not enable
if (_listenPort == null && _sslListenPort == null) {
System.err.println(USAGE);
throw new IllegalArgumentException(USAGE);
}
}
@ -96,27 +163,69 @@ public class RouterConsoleRunner {
List<String> notStarted = new ArrayList();
WebApplicationHandler baseHandler = null;
try {
StringTokenizer tok = new StringTokenizer(_listenHost, " ,");
int boundAddresses = 0;
while (tok.hasMoreTokens()) {
String host = tok.nextToken().trim();
try {
if (host.indexOf(":") >= 0) // IPV6 - requires patched Jetty 5
_server.addListener('[' + host + "]:" + _listenPort);
else
_server.addListener(host + ':' + _listenPort);
boundAddresses++;
} catch (IOException ioe) { // this doesn't seem to work, exceptions don't happen until start() below
System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ' ' + ioe);
// add standard listeners
if (_listenPort != null) {
StringTokenizer tok = new StringTokenizer(_listenHost, " ,");
while (tok.hasMoreTokens()) {
String host = tok.nextToken().trim();
try {
if (host.indexOf(":") >= 0) // IPV6 - requires patched Jetty 5
_server.addListener('[' + host + "]:" + _listenPort);
else
_server.addListener(host + ':' + _listenPort);
boundAddresses++;
} catch (IOException ioe) { // this doesn't seem to work, exceptions don't happen until start() below
System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ' ' + ioe);
}
}
}
// add SSL listeners
int sslPort = 0;
if (_sslListenPort != null) {
try {
sslPort = Integer.parseInt(_sslListenPort);
} catch (NumberFormatException nfe) {}
if (sslPort <= 0)
System.err.println("Bad routerconsole SSL port " + _sslListenPort);
}
if (sslPort > 0) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
File keyStore = new File(ctx.getConfigDir(), "keystore/console.ks");
if (verifyKeyStore(keyStore)) {
StringTokenizer tok = new StringTokenizer(_sslListenHost, " ,");
while (tok.hasMoreTokens()) {
String host = tok.nextToken().trim();
// doing it this way means we don't have to escape an IPv6 host with []
InetAddrPort iap = new InetAddrPort(host, sslPort);
try {
SslListener ssll = new SslListener(iap);
// the keystore path and password
ssll.setKeystore(keyStore.getAbsolutePath());
ssll.setPassword(ctx.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
// the X.509 cert password (if not present, verifyKeyStore() returned false)
ssll.setKeyPassword(ctx.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
_server.addListener(ssll);
boundAddresses++;
} catch (Exception e) { // probably no exceptions at this point
System.err.println("Unable to bind routerconsole to " + host + " port " + sslPort + " for SSL: " + e);
}
}
} else {
System.err.println("Unable to create or access keystore for SSL: " + keyStore.getAbsolutePath());
}
}
if (boundAddresses <= 0) {
System.err.println("Unable to bind routerconsole to any address on port " + _listenPort);
System.err.println("Unable to bind routerconsole to any address on port " + _listenPort + (sslPort > 0 ? (" or SSL port " + sslPort) : ""));
return;
}
_server.setRootWebApp(ROUTERCONSOLE);
WebApplicationContext wac = _server.addWebApplication("/", _webAppsDir + ROUTERCONSOLE + ".war");
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" + _listenPort);
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" +
(_listenPort != null ? _listenPort : _sslListenPort));
tmpdir.mkdir();
wac.setTempDirectory(tmpdir);
baseHandler = new LocaleWebAppHandler(I2PAppContext.getGlobalContext());
@ -131,7 +240,8 @@ public class RouterConsoleRunner {
String enabled = props.getProperty(PREFIX + appName + ENABLED);
if (! "false".equals(enabled)) {
String path = new File(dir, fileNames[i]).getCanonicalPath();
tmpdir = new SecureDirectory(workDir, appName + "-" + _listenPort);
tmpdir = new SecureDirectory(workDir, appName + "-" +
(_listenPort != null ? _listenPort : _sslListenPort));
WebAppStarter.addWebApp(I2PAppContext.getGlobalContext(), _server, appName, path, tmpdir);
if (enabled == null) {
@ -201,6 +311,90 @@ public class RouterConsoleRunner {
}
}
/**
* @return success if it exists and we have a password, or it was created successfully.
* @since 0.8.3
*/
private static boolean verifyKeyStore(File ks) {
if (ks.exists()) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
boolean rv = ctx.getProperty(PROP_KEY_PASSWORD) != null;
if (!rv)
System.err.println("Console SSL error, must set " + PROP_KEY_PASSWORD + " in " + (new File(ctx.getConfigDir(), "router.config")).getAbsolutePath());
return rv;
}
File dir = ks.getParentFile();
if (!dir.exists()) {
File sdir = new SecureDirectory(dir.getAbsolutePath());
if (!sdir.mkdir())
return false;
}
return createKeyStore(ks);
}
/**
* Call out to keytool to create a new keystore with a keypair in it.
* Trying to do this programatically is a nightmare, requiring either BouncyCastle
* libs or using proprietary Sun libs, and it's a huge mess.
*
* @return success
* @since 0.8.3
*/
private static boolean createKeyStore(File ks) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
// make a random 48 character password (30 * 8 / 5)
byte[] rand = new byte[30];
ctx.random().nextBytes(rand);
String keyPassword = Base32.encode(rand);
// and one for the cname
ctx.random().nextBytes(rand);
String cname = Base32.encode(rand) + ".console.i2p.net";
String keytool = (new File(System.getProperty("java.home"), "bin/keytool")).getAbsolutePath();
String[] args = new String[] {
keytool,
"-genkey", // -genkeypair preferred in newer keytools, but this works with more
"-storetype", KeyStore.getDefaultType(),
"-keystore", ks.getAbsolutePath(),
"-storepass", DEFAULT_KEYSTORE_PASSWORD,
"-alias", "console",
"-dname", "CN=" + cname + ",OU=Console,O=I2P Anonymous Network,L=XX,ST=XX,C=XX",
"-validity", "3652", // 10 years
"-keyalg", "DSA",
"-keysize", "1024",
"-keypass", keyPassword};
boolean success = (new ShellCommand()).executeSilentAndWaitTimed(args, 30); // 30 secs
if (success) {
success = ks.exists();
if (success) {
SecureFileOutputStream.setPerms(ks);
try {
RouterContext rctx = (RouterContext) ctx;
rctx.router().setConfigSetting(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
rctx.router().setConfigSetting(PROP_KEY_PASSWORD, keyPassword);
rctx.router().saveConfig();
} catch (Exception e) {} // class cast exception
}
}
if (success) {
System.err.println("Created self-signed certificate for " + cname + " in keystore: " + ks.getAbsolutePath() + "\n" +
"The certificate name was generated randomly, and is not associated with your " +
"IP address, host name, router identity, or destination keys.");
} else {
System.err.println("Failed to create console SSL keystore using command line:");
StringBuilder buf = new StringBuilder(256);
for (int i = 0; i < args.length; i++) {
buf.append('"').append(args[i]).append("\" ");
}
System.err.println(buf.toString());
System.err.println("This is for the Sun/Oracle keytool, others may be incompatible.\n" +
"If you create the keystore manually, you must add " + PROP_KEYSTORE_PASSWORD + " and " + PROP_KEY_PASSWORD +
" to " + (new File(ctx.getConfigDir(), "router.config")).getAbsolutePath());
}
return success;
}
static void initialize(WebApplicationContext context) {
String password = getPassword();
if (password != null) {

View File

@ -1,8 +1,11 @@
package net.i2p.router.web;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
@ -15,10 +18,12 @@ import net.i2p.router.RouterVersion;
import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.PartialEepGet;
import net.i2p.util.VersionComparator;
/**
* <p>Handles the request to update the router by firing off an
* {@link net.i2p.util.EepGet} call to download the latest signed update file
* <p>Handles the request to update the router by firing one or more
* {@link net.i2p.util.EepGet} calls to download the latest signed update file
* and displaying the status to anyone who asks.
* </p>
* <p>After the download completes the signed update file is verified with
@ -125,6 +130,11 @@ public class UpdateHandler {
protected boolean done;
protected EepGet _get;
protected final DecimalFormat _pct = new DecimalFormat("0.0%");
/** tells the listeners what mode we are in */
private boolean _isPartial;
/** set by the listeners on completion */
private boolean _isNewer;
private ByteArrayOutputStream _baos;
public UpdateRunner() {
_isRunning = false;
@ -141,39 +151,88 @@ public class UpdateHandler {
System.setProperty(PROP_UPDATE_IN_PROGRESS, "false");
_isRunning = false;
}
/**
* Loop through the entire list of update URLs.
* For each one, first get the version from the first 56 bytes and see if
* it is newer than what we are running now.
* If it is, get the whole thing.
*/
protected void update() {
updateStatus("<b>" + _("Updating") + "</b>");
// TODO:
// Do a PartialEepGet on the selected URL, check for version we expect,
// and loop if it isn't what we want.
// This will allow us to do a release without waiting for the last host to install the update.
// Alternative: In bytesTransferred(), Check the data in the output file after
// we've received at least 56 bytes. Need a cancel() method in EepGet ?
String updateURL = selectUpdateURL();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Selected update URL: " + updateURL);
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
int proxyPort = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
try {
if (shouldProxy)
// 40 retries!!
_get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false);
else
_get = new EepGet(_context, 1, _updateFile, updateURL, false);
_get.addStatusListener(UpdateRunner.this);
_get.fetch();
} catch (Throwable t) {
_log.error("Error updating", t);
List<String> urls = getUpdateURLs();
if (urls.isEmpty()) {
// not likely, don't bother translating
updateStatus("<b>Update source list is empty, cannot download update</b>");
_log.log(Log.CRIT, "Update source list is empty - cannot download update");
return;
}
if (shouldProxy)
_baos = new ByteArrayOutputStream(TrustedUpdate.HEADER_BYTES);
for (String updateURL : urls) {
updateStatus("<b>" + _("Updating from {0}", linkify(updateURL)) + "</b>");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Selected update URL: " + updateURL);
// Check the first 56 bytes for the version
if (shouldProxy) {
_isPartial = true;
_isNewer = false;
_baos.reset();
try {
// no retries
_get = new PartialEepGet(_context, proxyHost, proxyPort, _baos, updateURL, TrustedUpdate.HEADER_BYTES);
_get.addStatusListener(UpdateRunner.this);
_get.fetch();
} catch (Throwable t) {
_isNewer = false;
}
_isPartial = false;
if (!_isNewer)
continue;
}
// Now get the whole thing
try {
if (shouldProxy)
// 40 retries!!
_get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false);
else
_get = new EepGet(_context, 1, _updateFile, updateURL, false);
_get.addStatusListener(UpdateRunner.this);
_get.fetch();
} catch (Throwable t) {
_log.error("Error updating", t);
}
if (this.done)
break;
}
}
// EepGet Listeners below.
// We use the same for both the partial and the full EepGet,
// with a couple of adjustments depending on which mode.
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_isNewer = false;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Attempt failed on " + url, cause);
// ignored
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
if (_isPartial)
return;
StringBuilder buf = new StringBuilder(64);
buf.append("<b>").append(_("Updating")).append("</b> ");
double pct = ((double)alreadyTransferred + (double)currentWrite) /
@ -186,6 +245,19 @@ public class UpdateHandler {
updateStatus(buf.toString());
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
if (_isPartial) {
// Compare version with what we have now
String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray()));
boolean newer = (new VersionComparator()).compare(newVersion, RouterVersion.VERSION) > 0;
if (!newer) {
updateStatus("<b>" + _("No new version found at {0}", linkify(url)) + "</b>");
if (_log.shouldLog(Log.WARN))
_log.warn("Found old version \"" + newVersion + "\" at " + url);
}
_isNewer = newer;
return;
}
// Process the .sud/.su2 file
updateStatus("<b>" + _("Update downloaded") + "</b>");
TrustedUpdate up = new TrustedUpdate(_context);
File f = new File(_updateFile);
@ -223,15 +295,16 @@ public class UpdateHandler {
}
} else {
_log.log(Log.CRIT, err + " from " + url);
updateStatus("<b>" + err + ' ' + _("from {0}", url) + " </b>");
updateStatus("<b>" + err + ' ' + _("from {0}", linkify(url)) + " </b>");
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_isNewer = false;
// don't display bytesTransferred as it is meaningless
_log.log(Log.CRIT, "Update from " + url + " did not download completely (" +
_log.error("Update from " + url + " did not download completely (" +
bytesRemaining + " remaining after " + currentAttempt + " tries)");
updateStatus("<b>" + _("Transfer failed") + "</b>");
updateStatus("<b>" + _("Transfer failed from {0}", linkify(url)) + "</b>");
}
public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
@ -242,27 +315,24 @@ public class UpdateHandler {
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
}
private String selectUpdateURL() {
private List<String> getUpdateURLs() {
String URLs = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL, ConfigUpdateHandler.DEFAULT_UPDATE_URL);
StringTokenizer tok = new StringTokenizer(URLs, " ,\r\n");
List URLList = new ArrayList();
List<String> URLList = new ArrayList();
while (tok.hasMoreTokens())
URLList.add(tok.nextToken().trim());
int size = URLList.size();
//_log.log(Log.DEBUG, "Picking update source from " + size + " candidates.");
if (size <= 0) {
_log.log(Log.CRIT, "Update source list is empty - cannot download update");
return null;
}
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
_log.log(Log.DEBUG, "Picked update source " + index + ".");
return (String) URLList.get(index);
Collections.shuffle(URLList, _context.random());
return URLList;
}
protected void updateStatus(String s) {
_status = s;
}
protected static String linkify(String url) {
return "<a target=\"_blank\" href=\"" + url + "\"/>" + url + "</a>";
}
/** translate a string */
protected String _(String s) {
return Messages.getString(s, _context);