* add new and generally ugly components to allow web based control of tunnels
* build an i2ptunnel.war
This commit is contained in:
@ -22,6 +22,12 @@
|
||||
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<ant target="war" />
|
||||
</target>
|
||||
<target name="war">
|
||||
<war destfile="build/i2ptunnel.war" webxml="../jsp/web.xml"
|
||||
basedir="../jsp/" excludes="web.xml">
|
||||
</war>
|
||||
</target>
|
||||
<target name="javadoc">
|
||||
<mkdir dir="./build" />
|
||||
|
337
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
Normal file
337
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
Normal file
@ -0,0 +1,337 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Coordinate the runtime operation and configuration of a tunnel.
|
||||
* These objects are bundled together under a TunnelControllerGroup where the
|
||||
* entire group is stored / loaded from a single config file.
|
||||
*
|
||||
*/
|
||||
public class TunnelController implements Logging {
|
||||
private Log _log;
|
||||
private Properties _config;
|
||||
private I2PTunnel _tunnel;
|
||||
private List _messages;
|
||||
private boolean _running;
|
||||
|
||||
/**
|
||||
* Create a new controller for a tunnel out of the specific config options.
|
||||
* The config may contain a large number of options - only ones that begin in
|
||||
* the prefix should be used (and, in turn, that prefix should be stripped off
|
||||
* before being interpreted by this controller)
|
||||
*
|
||||
* @param config original key=value mapping
|
||||
* @param prefix beginning of key values that are relevent to this tunnel
|
||||
*/
|
||||
public TunnelController(Properties config, String prefix) {
|
||||
this(config, prefix, false);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param createKey for servers, whether we want to create a brand new destination
|
||||
* with private keys at the location specified or not (does not
|
||||
* overwrite existing ones)
|
||||
*/
|
||||
public TunnelController(Properties config, String prefix, boolean createKey) {
|
||||
_tunnel = new I2PTunnel();
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelController.class);
|
||||
setConfig(config, prefix);
|
||||
_messages = new ArrayList(4);
|
||||
_running = false;
|
||||
if (createKey)
|
||||
createPrivateKey();
|
||||
}
|
||||
|
||||
private void createPrivateKey() {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
File keyFile = new File(getPrivKeyFile());
|
||||
if (keyFile.exists()) {
|
||||
log("Not overwriting existing private keys in " + keyFile.getAbsolutePath());
|
||||
return;
|
||||
} else {
|
||||
File parent = keyFile.getParentFile();
|
||||
if ( (parent != null) && (!parent.exists()) )
|
||||
parent.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(keyFile);
|
||||
Destination dest = client.createDestination(fos);
|
||||
String destStr = dest.toBase64();
|
||||
log("Private key created and saved in " + keyFile.getAbsolutePath());
|
||||
log("New destination: " + destStr);
|
||||
} catch (I2PException ie) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating new destination", ie);
|
||||
log("Error creating new destination: " + ie.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating writing the destination to " + keyFile.getAbsolutePath(), ioe);
|
||||
log("Error writing the keys to " + keyFile.getAbsolutePath());
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up the tunnel (if it isn't already running)
|
||||
*
|
||||
*/
|
||||
public void startTunnel() {
|
||||
if (_running) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Already running");
|
||||
log("Tunnel " + getName() + " is already running");
|
||||
return;
|
||||
}
|
||||
String type = getType();
|
||||
if ( (type == null) || (type.length() <= 0) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Cannot start the tunnel - no type specified");
|
||||
return;
|
||||
}
|
||||
if ("httpclient".equals(type)) {
|
||||
startHttpClient();
|
||||
} else if ("client".equals(type)) {
|
||||
startClient();
|
||||
} else if ("server".equals(type)) {
|
||||
startServer();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.error("Cannot start tunnel - unknown type [" + type + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private void startHttpClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String proxyList = getProxyList();
|
||||
if (proxyList == null)
|
||||
_tunnel.runHttpClient(new String[] { listenPort }, this);
|
||||
else
|
||||
_tunnel.runHttpClient(new String[] { listenPort, proxyList }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String dest = getTargetDestination();
|
||||
_tunnel.runClient(new String[] { listenPort, dest }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startServer() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
String targetHost = getTargetHost();
|
||||
String targetPort = getTargetPort();
|
||||
String privKeyFile = getPrivKeyFile();
|
||||
_tunnel.runServer(new String[] { targetHost, targetPort, privKeyFile }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void setListenOn() {
|
||||
String listenOn = getListenOnInterface();
|
||||
if ( (listenOn != null) && (listenOn.length() > 0) ) {
|
||||
_tunnel.runListenOn(new String[] { listenOn }, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSessionOptions() {
|
||||
List opts = new ArrayList();
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
if (key.startsWith("option.")) {
|
||||
key = key.substring("option.".length());
|
||||
opts.add(key + "=" + val);
|
||||
}
|
||||
}
|
||||
String args[] = new String[opts.size()];
|
||||
for (int i = 0; i < opts.size(); i++)
|
||||
args[i] = (String)opts.get(i);
|
||||
_tunnel.runClientOptions(args, this);
|
||||
}
|
||||
|
||||
private void setI2CPOptions() {
|
||||
String host = getI2CPHost();
|
||||
if ( (host != null) && (host.length() > 0) )
|
||||
_tunnel.host = host;
|
||||
String port = getI2CPPort();
|
||||
if ( (port != null) && (port.length() > 0) )
|
||||
_tunnel.port = port;
|
||||
}
|
||||
|
||||
public void stopTunnel() {
|
||||
_tunnel.runClose(new String[] { "forced", "all" }, this);
|
||||
_running = false;
|
||||
}
|
||||
|
||||
public void restartTunnel() {
|
||||
stopTunnel();
|
||||
startTunnel();
|
||||
}
|
||||
|
||||
public void setConfig(Properties config, String prefix) {
|
||||
Properties props = new Properties();
|
||||
for (Iterator iter = config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = config.getProperty(key);
|
||||
if (key.startsWith(prefix)) {
|
||||
key = key.substring(prefix.length());
|
||||
props.setProperty(key, val);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Set prop [" + key + "] to [" + val + "]");
|
||||
}
|
||||
}
|
||||
_config = props;
|
||||
}
|
||||
public Properties getConfig(String prefix) {
|
||||
Properties rv = new Properties();
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
rv.setProperty(prefix + key, val);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public String getType() { return _config.getProperty("type"); }
|
||||
public String getName() { return _config.getProperty("name"); }
|
||||
public String getDescription() { return _config.getProperty("description"); }
|
||||
public String getI2CPHost() { return _config.getProperty("i2cpHost"); }
|
||||
public String getI2CPPort() { return _config.getProperty("i2cpPort"); }
|
||||
public String getClientOptions() {
|
||||
StringBuffer opts = new StringBuffer(64);
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
if (key.startsWith("option.")) {
|
||||
key = key.substring("option.".length());
|
||||
if (opts.length() > 0) opts.append(' ');
|
||||
opts.append(key).append('=').append(val);
|
||||
}
|
||||
}
|
||||
return opts.toString();
|
||||
}
|
||||
public String getListenOnInterface() { return _config.getProperty("interface"); }
|
||||
public String getTargetHost() { return _config.getProperty("targetHost"); }
|
||||
public String getTargetPort() { return _config.getProperty("targetPort"); }
|
||||
public String getPrivKeyFile() { return _config.getProperty("privKeyFile"); }
|
||||
public String getListenPort() { return _config.getProperty("listenPort"); }
|
||||
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
|
||||
public String getProxyList() { return _config.getProperty("proxyList"); }
|
||||
|
||||
public boolean getIsRunning() { return _running; }
|
||||
|
||||
public void getSummary(StringBuffer buf) {
|
||||
String type = getType();
|
||||
if ("httpclient".equals(type))
|
||||
getHttpClientSummary(buf);
|
||||
else if ("client".equals(type))
|
||||
getClientSummary(buf);
|
||||
else if ("server".equals(type))
|
||||
getServerSummary(buf);
|
||||
else
|
||||
buf.append("Unknown type ").append(type);
|
||||
}
|
||||
|
||||
private void getHttpClientSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("HTTP proxy listening on port ").append(getListenPort());
|
||||
String listenOn = getListenOnInterface();
|
||||
if ("0.0.0.0".equals(listenOn))
|
||||
buf.append(" (reachable by any machine)");
|
||||
else if ("127.0.0.1".equals(listenOn))
|
||||
buf.append(" (reachable locally only)");
|
||||
else
|
||||
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
|
||||
buf.append("<br />\n");
|
||||
String proxies = getProxyList();
|
||||
if ( (proxies == null) || (proxies.trim().length() <= 0) )
|
||||
buf.append("Outproxy: default [squid.i2p]<br />\n");
|
||||
else
|
||||
buf.append("Outproxy: ").append(proxies).append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getClientSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("Client tunnel listening on port ").append(getListenPort());
|
||||
buf.append(" pointing at ").append(getTargetDestination());
|
||||
String listenOn = getListenOnInterface();
|
||||
if ("0.0.0.0".equals(listenOn))
|
||||
buf.append(" (reachable by any machine)");
|
||||
else if ("127.0.0.1".equals(listenOn))
|
||||
buf.append(" (reachable locally only)");
|
||||
else
|
||||
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
|
||||
buf.append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getServerSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("Server tunnel pointing at port ").append(getTargetPort());
|
||||
buf.append(" on ").append(getTargetHost());
|
||||
buf.append("<br />\n");
|
||||
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getOptionSummary(StringBuffer buf) {
|
||||
String opts = getClientOptions();
|
||||
if ( (opts != null) && (opts.length() > 0) )
|
||||
buf.append("Network options: ").append(opts).append("<br />\n");
|
||||
}
|
||||
|
||||
public void log(String s) {
|
||||
synchronized (this) {
|
||||
_messages.add(s);
|
||||
while (_messages.size() > 10)
|
||||
_messages.remove(0);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull off any messages that the I2PTunnel has produced
|
||||
*
|
||||
* @return list of messages pulled off (each is a String, earliest first)
|
||||
*/
|
||||
public List clearMessages() {
|
||||
List rv = null;
|
||||
synchronized (this) {
|
||||
rv = new ArrayList(_messages);
|
||||
_messages.clear();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
@ -0,0 +1,249 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Coordinate a set of tunnels within the JVM, loading and storing their config
|
||||
* to disk, and building new ones as requested.
|
||||
*
|
||||
*/
|
||||
public class TunnelControllerGroup {
|
||||
private Log _log;
|
||||
private List _controllers;
|
||||
private static TunnelControllerGroup _instance = new TunnelControllerGroup();
|
||||
public static TunnelControllerGroup getInstance() { return _instance; }
|
||||
|
||||
private TunnelControllerGroup() {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class);
|
||||
_controllers = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load up all of the tunnels configured in the given file (but do not start
|
||||
* them)
|
||||
*
|
||||
*/
|
||||
public void loadControllers(String configFile) {
|
||||
Properties cfg = loadConfig(configFile);
|
||||
if (cfg == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to load the config from " + configFile);
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
while (true) {
|
||||
String type = cfg.getProperty("tunnel." + i + ".type");
|
||||
if (type == null)
|
||||
break;
|
||||
TunnelController controller = new TunnelController(cfg, "tunnel." + i + ".");
|
||||
_controllers.add(controller);
|
||||
i++;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(i + " controllers loaded from " + configFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop and remove reference to all known tunnels (but dont delete any config
|
||||
* file or do other silly things)
|
||||
*
|
||||
*/
|
||||
public void unloadControllers() {
|
||||
stopAllControllers();
|
||||
_controllers.clear();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("All controllers stopped and unloaded");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given tunnel to the set of known controllers (but dont add it to
|
||||
* a config file or start it or anything)
|
||||
*
|
||||
*/
|
||||
public void addController(TunnelController controller) { _controllers.add(controller); }
|
||||
|
||||
/**
|
||||
* Stop and remove the given tunnel
|
||||
*
|
||||
* @return list of messages from the controller as it is stopped
|
||||
*/
|
||||
public List removeController(TunnelController controller) {
|
||||
if (controller == null) return new ArrayList();
|
||||
controller.stopTunnel();
|
||||
List msgs = controller.clearMessages();
|
||||
_controllers.remove(controller);
|
||||
msgs.add("Tunnel " + controller.getName() + " removed");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when stopped
|
||||
*/
|
||||
public List stopAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.stopTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers stopped");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when started
|
||||
*/
|
||||
public List startAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.startTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers started");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when restarted
|
||||
*/
|
||||
public List restartAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.restartTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers restarted");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all outstanding messages from any of the known tunnels
|
||||
*
|
||||
* @return list of messages the tunnels have generated
|
||||
*/
|
||||
public List clearAllMessages() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the configuration of all known tunnels to the given file
|
||||
*
|
||||
*/
|
||||
public void saveConfig(String configFile) {
|
||||
File cfgFile = new File(configFile);
|
||||
File parent = cfgFile.getParentFile();
|
||||
if ( (parent != null) && (!parent.exists()) )
|
||||
parent.mkdirs();
|
||||
|
||||
|
||||
TreeMap map = new TreeMap();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
Properties cur = controller.getConfig("tunnel." + i + ".");
|
||||
map.putAll(cur);
|
||||
}
|
||||
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = (String)map.get(key);
|
||||
buf.append(key).append('=').append(val).append('\n');
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(cfgFile);
|
||||
fos.write(buf.toString().getBytes());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Config written to " + cfgFile.getPath());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing out the config");
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load up the config data from the file
|
||||
*
|
||||
* @return properties loaded or null if there was an error
|
||||
*/
|
||||
private Properties loadConfig(String configFile) {
|
||||
File cfgFile = new File(configFile);
|
||||
if (!cfgFile.exists()) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Unable to load the controllers from " + configFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
Properties props = new Properties();
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(cfgFile);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.length() <= 0) continue;
|
||||
if (line.startsWith("#") || line.startsWith(";"))
|
||||
continue;
|
||||
int eq = line.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= line.length() - 1) )
|
||||
continue;
|
||||
String key = line.substring(0, eq);
|
||||
String val = line.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Props loaded with " + props.size() + " lines");
|
||||
return props;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error reading the controllers from " + configFile, ioe);
|
||||
return null;
|
||||
} finally {
|
||||
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of tunnels known
|
||||
*
|
||||
* @return list of TunnelController objects
|
||||
*/
|
||||
public List getControllers() { return _controllers; }
|
||||
}
|
@ -0,0 +1,307 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Uuuugly... generate the edit/add forms for the various
|
||||
* I2PTunnel types (httpclient/client/server)
|
||||
*
|
||||
*/
|
||||
class WebEditPageFormGenerator {
|
||||
private static final String SELECT_TYPE_FORM =
|
||||
"<form action=\"edit.jsp\"> Type of tunnel: <select name=\"type\">" +
|
||||
"<option value=\"httpclient\">HTTP proxy</option>" +
|
||||
"<option value=\"client\">Client tunnel</option>" +
|
||||
"<option value=\"server\">Server tunnel</option>" +
|
||||
"</select> <input type=\"submit\" value=\"GO\" />" +
|
||||
"</form>\n";
|
||||
|
||||
/**
|
||||
* Retrieve the form requested
|
||||
*
|
||||
*/
|
||||
public static String getForm(WebEditPageHelper helper) {
|
||||
TunnelController controller = helper.getTunnelController();
|
||||
|
||||
if ( (helper.getType() == null) && (controller == null) )
|
||||
return SELECT_TYPE_FORM;
|
||||
|
||||
String id = helper.getNum();
|
||||
String type = helper.getType();
|
||||
if (controller != null)
|
||||
type = controller.getType();
|
||||
|
||||
if ("httpclient".equals(type))
|
||||
return getEditHttpClientForm(controller, id);
|
||||
else if ("client".equals(type))
|
||||
return getEditClientForm(controller, id);
|
||||
else if ("server".equals(type))
|
||||
return getEditServerForm(controller, id);
|
||||
else
|
||||
return "WTF, unknown type [" + type + "]";
|
||||
}
|
||||
|
||||
private static String getEditHttpClientForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>HTTP proxy</i><input type=\"hidden\" name=\"type\" value=\"httpclient\" /><br />\n");
|
||||
|
||||
addListeningOn(buf, controller, 4444);
|
||||
|
||||
buf.append("<b>Outproxies:</b> <input type=\"text\" name=\"proxyList\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getProxyList() != null) )
|
||||
buf.append("value=\"").append(controller.getProxyList()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"squid.i2p\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String getEditClientForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>Client tunnel</i><input type=\"hidden\" name=\"type\" value=\"client\" /><br />\n");
|
||||
|
||||
addListeningOn(buf, controller, 2025 + new Random().nextInt(1000)); // 2025 since nextInt can be negative
|
||||
|
||||
buf.append("<b>Target:</b> <input type=\"text\" size=\"40\" name=\"targetDestination\" ");
|
||||
if ( (controller != null) && (controller.getTargetDestination() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetDestination()).append("\" ");
|
||||
buf.append(" /> (either the hosts.txt name or the full base64 destination)<br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String getEditServerForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>Server tunnel</i><input type=\"hidden\" name=\"type\" value=\"server\" /><br />\n");
|
||||
|
||||
buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" ");
|
||||
if ( (controller != null) && (controller.getTargetHost() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"localhost\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
|
||||
if ( (controller != null) && (controller.getTargetPort() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetPort()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"80\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\"");
|
||||
if ( (controller != null) && (controller.getPrivKeyFile() != null) ) {
|
||||
buf.append(controller.getPrivKeyFile()).append("\" /><br />");
|
||||
} else {
|
||||
buf.append("myServer.privKey\" /><br />");
|
||||
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
|
||||
}
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start off the form and add some common fields (name, num, description)
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
* @param id index into the current list of tunnelControllerGroup.getControllers() list
|
||||
* (or null if we are generating an 'add' form)
|
||||
*/
|
||||
private static void addGeneral(StringBuffer buf, TunnelController controller, String id) {
|
||||
buf.append("<form action=\"edit.jsp\">");
|
||||
if (id != null)
|
||||
buf.append("<input type=\"hidden\" name=\"num\" value=\"").append(id).append("\" />");
|
||||
|
||||
buf.append("<b>Name:</b> <input type=\"text\" name=\"name\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getName() != null) )
|
||||
buf.append("value=\"").append(controller.getName()).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
buf.append("<b>Description:</b> <input type=\"text\" name=\"description\" size=\"60\" ");
|
||||
if ( (controller != null) && (controller.getDescription() != null) )
|
||||
buf.append("value=\"").append(controller.getDescription()).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the fields asking for what port and interface the tunnel should
|
||||
* listen on.
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
* @param defaultPort if we are creating a new tunnel, default the form to the given port
|
||||
*/
|
||||
private static void addListeningOn(StringBuffer buf, TunnelController controller, int defaultPort) {
|
||||
buf.append("<b>Listening on port:</b> <input type=\"text\" name=\"port\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getListenPort() != null) )
|
||||
buf.append("value=\"").append(controller.getListenPort()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"").append(defaultPort).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
String selectedOn = null;
|
||||
if ( (controller != null) && (controller.getListenOnInterface() != null) )
|
||||
selectedOn = controller.getListenOnInterface();
|
||||
|
||||
buf.append("<b>Reachable by:</b> ");
|
||||
buf.append("<select name=\"reachableBy\">");
|
||||
buf.append("<option value=\"127.0.0.1\" ");
|
||||
if ( (selectedOn != null) && ("127.0.0.1".equals(selectedOn)) )
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Locally (127.0.0.1)</option>\n");
|
||||
buf.append("<option value=\"0.0.0.0\" ");
|
||||
if ( (selectedOn != null) && ("0.0.0.0".equals(selectedOn)) )
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Everyone (0.0.0.0)</option>\n");
|
||||
buf.append("</select> ");
|
||||
buf.append("Other: <input type=\"text\" name=\"reachableByOther\" value=\"");
|
||||
if ( (selectedOn != null) && (!"127.0.0.1".equals(selectedOn)) && (!"0.0.0.0".equals(selectedOn)) )
|
||||
buf.append(selectedOn);
|
||||
buf.append("\"><br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields for customizing the I2PSession options, including helpers for
|
||||
* tunnel depth and count, as well as I2CP host and port.
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
*/
|
||||
private static void addOptions(StringBuffer buf, TunnelController controller) {
|
||||
int tunnelDepth = 2;
|
||||
int numTunnels = 2;
|
||||
Properties opts = getOptions(controller);
|
||||
if (opts != null) {
|
||||
String depth = opts.getProperty("tunnels.depthInbound");
|
||||
if (depth != null) {
|
||||
try {
|
||||
tunnelDepth = Integer.parseInt(depth);
|
||||
} catch (NumberFormatException nfe) {
|
||||
tunnelDepth = 2;
|
||||
}
|
||||
}
|
||||
String num = opts.getProperty("tunnels.numInbound");
|
||||
if (num != null) {
|
||||
try {
|
||||
numTunnels = Integer.parseInt(num);
|
||||
} catch (NumberFormatException nfe) {
|
||||
numTunnels = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.append("<b>Tunnel depth:</b> ");
|
||||
buf.append("<select name=\"tunnelDepth\">");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (tunnelDepth == 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 hop tunnel (low anonymity, low latency)</option>");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (tunnelDepth == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 hop tunnel (medium anonymity, medium latency)</option>");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (tunnelDepth == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 hop tunnel (high anonymity, high latency)</option>");
|
||||
if (tunnelDepth > 2) {
|
||||
buf.append("<option value=\"").append(tunnelDepth).append("\" selected=\"true\" >");
|
||||
buf.append(tunnelDepth);
|
||||
buf.append(" hop tunnel (custom)</option>");
|
||||
}
|
||||
buf.append("</select><br />\n");
|
||||
|
||||
buf.append("<b>Tunnel count:</b> ");
|
||||
buf.append("<select name=\"tunnelCount\">");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (numTunnels == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 inbound tunnel (low bandwidth usage, less reliability)</option>");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (numTunnels == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 inbound tunnels (standard bandwidth usage, standard reliability)</option>");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (numTunnels == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 inbound tunnels (higher bandwidth usage, higher reliability)</option>");
|
||||
|
||||
if (numTunnels > 3) {
|
||||
buf.append("<option value=\"").append(numTunnels).append("\" selected=\"true\" >");
|
||||
buf.append(numTunnels);
|
||||
buf.append(" inbound tunnels (custom)</option>");
|
||||
}
|
||||
buf.append("</select><br />\n");
|
||||
|
||||
buf.append("<b>I2CP host:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientHost\" size=\"20\" value=\"");
|
||||
if ( (controller != null) && (controller.getI2CPHost() != null) )
|
||||
buf.append(controller.getI2CPHost());
|
||||
else
|
||||
buf.append("localhost");
|
||||
buf.append("\" /><br />\n");
|
||||
buf.append("<b>I2CP port:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\"");
|
||||
if ( (controller != null) && (controller.getI2CPPort() != null) )
|
||||
buf.append(controller.getI2CPPort());
|
||||
else
|
||||
buf.append("7654");
|
||||
buf.append("\" /><br />\n");
|
||||
|
||||
buf.append("<b>Other custom options:</b> \n");
|
||||
buf.append("<input type=\"text\" name=\"customOptions\" size=\"60\" value=\"");
|
||||
if (opts != null) {
|
||||
int i = 0;
|
||||
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = opts.getProperty(key);
|
||||
if ("tunnels.depthInbound".equals(key)) continue;
|
||||
if ("tunnels.numInbound".equals(key)) continue;
|
||||
if (i != 0) buf.append(' ');
|
||||
buf.append(key).append('=').append(val);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
buf.append("\" /><br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the client options from the tunnel
|
||||
*
|
||||
* @return map of name=val to be used as I2P session options
|
||||
*/
|
||||
private static Properties getOptions(TunnelController controller) {
|
||||
if (controller == null) return null;
|
||||
String opts = controller.getClientOptions();
|
||||
StringTokenizer tok = new StringTokenizer(opts);
|
||||
Properties props = new Properties();
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
int eq = pair.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= pair.length()) )
|
||||
continue;
|
||||
String key = pair.substring(0, eq);
|
||||
String val = pair.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
348
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java
Normal file
348
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java
Normal file
@ -0,0 +1,348 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* UUUUuuuuuugly glue code to handle bean interaction from the web, process
|
||||
* that data, and spit out the results (or the form requested). The basic
|
||||
* usage is to set any of the fields with data then query the bean via
|
||||
* getActionResults() which triggers the request processing (taking all the
|
||||
* provided data, doing what needs to be done) and returns the results of those
|
||||
* activites. Then a subsequent call to getEditForm() generates the HTML form
|
||||
* to either edit the currently selected tunnel (if specified) or add a new one.
|
||||
* This functionality is delegated to the WebEditPageFormGenerator.
|
||||
*
|
||||
*/
|
||||
public class WebEditPageHelper {
|
||||
private Log _log;
|
||||
private String _action;
|
||||
private String _type;
|
||||
private String _id;
|
||||
private String _name;
|
||||
private String _description;
|
||||
private String _i2cpHost;
|
||||
private String _i2cpPort;
|
||||
private String _tunnelDepth;
|
||||
private String _tunnelCount;
|
||||
private String _customOptions;
|
||||
private String _proxyList;
|
||||
private String _port;
|
||||
private String _reachableBy;
|
||||
private String _reachableByOther;
|
||||
private String _targetDestination;
|
||||
private String _targetHost;
|
||||
private String _targetPort;
|
||||
private String _privKeyFile;
|
||||
private boolean _privKeyGenerate;
|
||||
private boolean _removeConfirmed;
|
||||
|
||||
public WebEditPageHelper() {
|
||||
_action = null;
|
||||
_type = null;
|
||||
_id = null;
|
||||
_removeConfirmed = false;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebEditPageHelper.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for form submit - either "Save" or Remove"
|
||||
*/
|
||||
public void setAction(String action) {
|
||||
_action = (action != null ? action.trim() : null);
|
||||
}
|
||||
/**
|
||||
* What type of tunnel (httpclient, client, or server). This is
|
||||
* required when adding a new tunnel.
|
||||
*
|
||||
*/
|
||||
public void setType(String type) {
|
||||
_type = (type != null ? type.trim() : null);
|
||||
}
|
||||
/**
|
||||
* Which particular tunnel should be edited (index into the current
|
||||
* TunnelControllerGroup's getControllers() list). This is required
|
||||
* when editing a tunnel, but not when adding a new one.
|
||||
*
|
||||
*/
|
||||
public void setNum(String id) {
|
||||
_id = (id != null ? id.trim() : null);
|
||||
}
|
||||
String getType() { return _type; }
|
||||
String getNum() { return _id; }
|
||||
|
||||
/** Short name of the tunnel */
|
||||
public void setName(String name) {
|
||||
_name = (name != null ? name.trim() : null);
|
||||
}
|
||||
/** one line description */
|
||||
public void setDescription(String description) {
|
||||
_description = (description != null ? description.trim() : null);
|
||||
}
|
||||
/** I2CP host the router is on */
|
||||
public void setClientHost(String host) {
|
||||
_i2cpHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** I2CP port the router is on */
|
||||
public void setClientPort(String port) {
|
||||
_i2cpPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
/** how many hops to use for inbound tunnels */
|
||||
public void setTunnelDepth(String tunnelDepth) {
|
||||
_tunnelDepth = (tunnelDepth != null ? tunnelDepth.trim() : null);
|
||||
}
|
||||
/** how many parallel inbound tunnels to use */
|
||||
public void setTunnelCount(String tunnelCount) {
|
||||
_tunnelCount = (tunnelCount != null ? tunnelCount.trim() : null);
|
||||
}
|
||||
/** what I2P session overrides should be used */
|
||||
public void setCustomOptions(String customOptions) {
|
||||
_customOptions = (customOptions != null ? customOptions.trim() : null);
|
||||
}
|
||||
/** what HTTP outproxies should be used (httpclient specific) */
|
||||
public void setProxyList(String proxyList) {
|
||||
_proxyList = (proxyList != null ? proxyList.trim() : null);
|
||||
}
|
||||
/** what port should this client/httpclient listen on */
|
||||
public void setPort(String port) {
|
||||
_port = (port != null ? port.trim() : null);
|
||||
}
|
||||
/**
|
||||
* what interface should this client/httpclient listen on (unless
|
||||
* overridden by the setReachableByOther() field)
|
||||
*/
|
||||
public void setReachableBy(String reachableBy) {
|
||||
_reachableBy = (reachableBy != null ? reachableBy.trim() : null);
|
||||
}
|
||||
/**
|
||||
* If specified, defines the exact IP interface to listen for requests
|
||||
* on (in the case of client/httpclient tunnels)
|
||||
*/
|
||||
public void setReachableByOther(String reachableByOther) {
|
||||
_reachableByOther = (reachableByOther != null ? reachableByOther.trim() : null);
|
||||
}
|
||||
/** What peer does this client tunnel point at */
|
||||
public void setTargetDestination(String dest) {
|
||||
_targetDestination = (dest != null ? dest.trim() : null);
|
||||
}
|
||||
/** What host does this server tunnel point at */
|
||||
public void setTargetHost(String host) {
|
||||
_targetHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** What port does this server tunnel point at */
|
||||
public void setTargetPort(String port) {
|
||||
_targetPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
/** What filename is this server tunnel's private keys stored in */
|
||||
public void setPrivKeyFile(String file) {
|
||||
_privKeyFile = (file != null ? file.trim() : null);
|
||||
}
|
||||
/**
|
||||
* If called with any value, we want to generate a new destination
|
||||
* for this server tunnel. This won't cause any existing private keys
|
||||
* to be overwritten, however.
|
||||
*/
|
||||
public void setPrivKeyGenerate(String moo) {
|
||||
_privKeyGenerate = true;
|
||||
}
|
||||
/**
|
||||
* If called with any value (and the form submitted with action=Remove),
|
||||
* we really do want to stop and remove the tunnel.
|
||||
*/
|
||||
public void setRemoveConfirm(String moo) {
|
||||
_removeConfirmed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the form and display any resulting messages
|
||||
*
|
||||
*/
|
||||
public String getActionResults() {
|
||||
try {
|
||||
return processAction();
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error processing request", t);
|
||||
return "Internal error - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an HTML form to edit / create a tunnel according to the
|
||||
* specified fields
|
||||
*/
|
||||
public String getEditForm() {
|
||||
try {
|
||||
return WebEditPageFormGenerator.getForm(this);
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error retrieving edit form", t);
|
||||
return "Internal error - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the tunnel pointed to by the current id
|
||||
*
|
||||
*/
|
||||
TunnelController getTunnelController() {
|
||||
if (_id == null) return null;
|
||||
int id = -1;
|
||||
try {
|
||||
id = Integer.parseInt(_id);
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if ( (id < 0) || (id >= controllers.size()) )
|
||||
return null;
|
||||
else
|
||||
return (TunnelController)controllers.get(id);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid tunnel id [" + _id + "]", nfe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) )
|
||||
return "";
|
||||
if ("Save".equals(_action))
|
||||
return save();
|
||||
else if ("Remove".equals(_action))
|
||||
return remove();
|
||||
else
|
||||
return "Action <i>" + _action + "</i> unknown";
|
||||
}
|
||||
|
||||
private String remove() {
|
||||
if (!_removeConfirmed)
|
||||
return "Please confirm removal";
|
||||
|
||||
TunnelController cur = getTunnelController();
|
||||
if (cur == null)
|
||||
return "Invalid tunnel number";
|
||||
|
||||
List msgs = TunnelControllerGroup.getInstance().removeController(cur);
|
||||
msgs.addAll(doSave());
|
||||
return getMessages(msgs);
|
||||
}
|
||||
|
||||
private String save() {
|
||||
if (_type == null)
|
||||
return "<b>Invalid form submission (no type?)</b>";
|
||||
Properties config = getConfig();
|
||||
if (config == null)
|
||||
return "<b>Invalid params</b>";
|
||||
|
||||
TunnelController cur = getTunnelController();
|
||||
if (cur == null) {
|
||||
// creating new
|
||||
cur = new TunnelController(config, "", _privKeyGenerate);
|
||||
TunnelControllerGroup.getInstance().addController(cur);
|
||||
} else {
|
||||
cur.setConfig(config, "");
|
||||
}
|
||||
|
||||
|
||||
return getMessages(doSave());
|
||||
}
|
||||
private List doSave() {
|
||||
TunnelControllerGroup.getInstance().saveConfig(WebStatusPageHelper.CONFIG_FILE);
|
||||
return TunnelControllerGroup.getInstance().clearAllMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on all provided data, create a set of configuration parameters
|
||||
* suitable for use in a TunnelController. This will replace (not add to)
|
||||
* any existing parameters, so this should return a comprehensive mapping.
|
||||
*
|
||||
*/
|
||||
private Properties getConfig() {
|
||||
Properties config = new Properties();
|
||||
updateConfigGeneric(config);
|
||||
|
||||
if ("httpclient".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else
|
||||
config.setProperty("interface", _reachableBy);
|
||||
if (_proxyList != null)
|
||||
config.setProperty("proxyList", _proxyList);
|
||||
} else if ("client".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else
|
||||
config.setProperty("interface", _reachableBy);
|
||||
if (_targetDestination != null)
|
||||
config.setProperty("targetDestination", _targetDestination);
|
||||
} else if ("server".equals(_type)) {
|
||||
if (_targetHost != null)
|
||||
config.setProperty("targetHost", _targetHost);
|
||||
if (_targetPort != null)
|
||||
config.setProperty("targetPort", _targetPort);
|
||||
if (_privKeyFile != null)
|
||||
config.setProperty("privKeyFile", _privKeyFile);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private void updateConfigGeneric(Properties config) {
|
||||
config.setProperty("type", _type);
|
||||
if (_name != null)
|
||||
config.setProperty("name", _name);
|
||||
if (_description != null)
|
||||
config.setProperty("description", _description);
|
||||
if (_i2cpHost != null)
|
||||
config.setProperty("i2cpHost", _i2cpHost);
|
||||
if (_i2cpPort != null)
|
||||
config.setProperty("i2cpPort", _i2cpPort);
|
||||
|
||||
if (_customOptions != null) {
|
||||
StringTokenizer tok = new StringTokenizer(_customOptions);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
int eq = pair.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= pair.length()) )
|
||||
continue;
|
||||
String key = pair.substring(0, eq);
|
||||
String val = pair.substring(eq+1);
|
||||
if ("tunnels.numInbound".equals(key)) continue;
|
||||
if ("tunnels.depthInbound".equals(key)) continue;
|
||||
config.setProperty("option." + key, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (_tunnelCount != null)
|
||||
config.setProperty("option.tunnels.numInbound", _tunnelCount);
|
||||
if (_tunnelDepth != null)
|
||||
config.setProperty("option.tunnels.depthInbound", _tunnelDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty print the messages provided
|
||||
*
|
||||
*/
|
||||
private String getMessages(List msgs) {
|
||||
if (msgs == null) return "";
|
||||
int num = msgs.size();
|
||||
switch (num) {
|
||||
case 0: return "";
|
||||
case 1: return (String)msgs.get(0);
|
||||
default:
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<ul>");
|
||||
for (int i = 0; i < num; i++)
|
||||
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
|
||||
buf.append("</ul>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Ugly hack to let the web interface access the list of known tunnels and
|
||||
* control their operation. Any data submitted by setting properties are
|
||||
* acted upon by calling getActionResults() (which returns any messages
|
||||
* generated). In addition, the getSummaryList() generates the html for
|
||||
* summarizing all of the tunnels known, including both their status and the
|
||||
* links to edit, stop, or start them.
|
||||
*
|
||||
*/
|
||||
public class WebStatusPageHelper {
|
||||
private Log _log;
|
||||
private String _action;
|
||||
private int _controllerNum;
|
||||
private static boolean _configLoaded = false;
|
||||
|
||||
static final String CONFIG_FILE = "i2ptunnel.cfg";
|
||||
|
||||
public WebStatusPageHelper() {
|
||||
_action = null;
|
||||
_controllerNum = -1;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebStatusPageHelper.class);
|
||||
synchronized (WebStatusPageHelper.class) {
|
||||
if (!_configLoaded) {
|
||||
reloadConfig();
|
||||
_configLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
_action = action;
|
||||
}
|
||||
public void setNum(String num) {
|
||||
if (num != null) {
|
||||
try {
|
||||
_controllerNum = Integer.parseInt(num);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_controllerNum = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getActionResults() {
|
||||
try {
|
||||
return processAction();
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error processing web status", t);
|
||||
return "Internal error processing request - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public String getSummaryList() {
|
||||
StringBuffer buf = new StringBuffer(4*1024);
|
||||
buf.append("<ul>");
|
||||
List tunnels = TunnelControllerGroup.getInstance().getControllers();
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
buf.append("<li>\n");
|
||||
getSummary(buf, i, (TunnelController)tunnels.get(i));
|
||||
buf.append("</li>\n");
|
||||
}
|
||||
buf.append("</ul>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private void getSummary(StringBuffer buf, int num, TunnelController controller) {
|
||||
buf.append("<b>").append(controller.getName()).append("</b>: ");
|
||||
if (controller.getIsRunning()) {
|
||||
buf.append("<i>running</i> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num).append("&action=stop\">stop</a> ");
|
||||
} else {
|
||||
buf.append("<i>not running</i> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num).append("&action=start\">start</a> ");
|
||||
}
|
||||
buf.append("<a href=\"edit.jsp?num=").append(num).append("\">edit</a> ");
|
||||
buf.append("<br />\n");
|
||||
controller.getSummary(buf);
|
||||
}
|
||||
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) )
|
||||
return getMessages();
|
||||
if ("Stop all".equals(_action))
|
||||
return stopAll();
|
||||
else if ("Start all".equals(_action))
|
||||
return startAll();
|
||||
else if ("Restart all".equals(_action))
|
||||
return restartAll();
|
||||
else if ("Reload config".equals(_action))
|
||||
return reloadConfig();
|
||||
else if ("stop".equals(_action))
|
||||
return stop();
|
||||
else if ("start".equals(_action))
|
||||
return start();
|
||||
else
|
||||
return "Action <i>" + _action + "</i> unknown";
|
||||
}
|
||||
private String stopAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().stopAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String startAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().startAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String restartAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().restartAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String reloadConfig() {
|
||||
TunnelControllerGroup.getInstance().unloadControllers();
|
||||
TunnelControllerGroup.getInstance().loadControllers(CONFIG_FILE);
|
||||
return "Config reloaded";
|
||||
}
|
||||
private String start() {
|
||||
if (_controllerNum < 0) return "Invalid tunnel";
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
|
||||
controller.startTunnel();
|
||||
return getMessages(controller.clearMessages());
|
||||
}
|
||||
|
||||
private String stop() {
|
||||
if (_controllerNum < 0) return "Invalid tunnel";
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
|
||||
controller.stopTunnel();
|
||||
return getMessages(controller.clearMessages());
|
||||
}
|
||||
|
||||
private String getMessages() {
|
||||
return getMessages(TunnelControllerGroup.getInstance().clearAllMessages());
|
||||
}
|
||||
|
||||
private String getMessages(List msgs) {
|
||||
if (msgs == null) return "";
|
||||
int num = msgs.size();
|
||||
switch (num) {
|
||||
case 0: return "";
|
||||
case 1: return (String)msgs.get(0);
|
||||
default:
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<ul>");
|
||||
for (int i = 0; i < num; i++)
|
||||
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
|
||||
buf.append("</ul>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
16
apps/i2ptunnel/jsp/edit.jsp
Normal file
16
apps/i2ptunnel/jsp/edit.jsp
Normal file
@ -0,0 +1,16 @@
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2PTunnel edit</title>
|
||||
</head><body>
|
||||
|
||||
<a href="index.jsp">Back</a>
|
||||
|
||||
<jsp:useBean class="net.i2p.i2ptunnel.WebEditPageHelper" id="helper" scope="request" />
|
||||
<jsp:setProperty name="helper" property="*" />
|
||||
<b><jsp:getProperty name="helper" property="actionResults" /></b>
|
||||
|
||||
<jsp:getProperty name="helper" property="editForm" />
|
||||
</body>
|
||||
</html>
|
31
apps/i2ptunnel/jsp/index.jsp
Normal file
31
apps/i2ptunnel/jsp/index.jsp
Normal file
@ -0,0 +1,31 @@
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2PTunnel status</title>
|
||||
</head><body>
|
||||
|
||||
<jsp:useBean class="net.i2p.i2ptunnel.WebStatusPageHelper" id="helper" scope="request" />
|
||||
<jsp:setProperty name="helper" property="*" />
|
||||
<b><jsp:getProperty name="helper" property="actionResults" /></b>
|
||||
|
||||
<jsp:getProperty name="helper" property="summaryList" />
|
||||
<hr />
|
||||
<form action="index.jsp" method="GET">
|
||||
<input type="submit" name="action" value="Stop all" />
|
||||
<input type="submit" name="action" value="Start all" />
|
||||
<input type="submit" name="action" value="Restart all" />
|
||||
<input type="submit" name="action" value="Reload config" />
|
||||
</form>
|
||||
|
||||
<form action="edit.jsp">
|
||||
<b>Add new:</b>
|
||||
<select name="type">
|
||||
<option value="httpclient">HTTP proxy</option>
|
||||
<option value="client">Client tunnel</option>
|
||||
<option value="server">Server tunnel</option>
|
||||
</select> <input type="submit" value="GO" />
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
17
apps/i2ptunnel/jsp/web.xml
Normal file
17
apps/i2ptunnel/jsp/web.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE web-app
|
||||
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
||||
|
||||
<web-app>
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
</session-config>
|
||||
<welcome-file-list>
|
||||
<welcome-file>
|
||||
index.jsp
|
||||
</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
@ -37,6 +37,7 @@
|
||||
<copy file="router/java/build/router.jar" todir="build/" />
|
||||
<copy file="apps/ministreaming/java/build/mstreaming.jar" todir="build/" />
|
||||
<copy file="apps/i2ptunnel/java/build/i2ptunnel.jar" todir="build/" />
|
||||
<copy file="apps/i2ptunnel/java/build/i2ptunnel.war" todir="build/" />
|
||||
<copy file="apps/httptunnel/java/build/httptunnel.jar" todir="build/" />
|
||||
<copy file="apps/phttprelay/java/build/phttprelay.war" todir="build/" />
|
||||
<copy file="apps/sam/java/build/sam.jar" todir="build/" />
|
||||
|
Reference in New Issue
Block a user