* 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" />
|
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
|
||||||
</manifest>
|
</manifest>
|
||||||
</jar>
|
</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>
|
||||||
<target name="javadoc">
|
<target name="javadoc">
|
||||||
<mkdir dir="./build" />
|
<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="router/java/build/router.jar" todir="build/" />
|
||||||
<copy file="apps/ministreaming/java/build/mstreaming.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.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/httptunnel/java/build/httptunnel.jar" todir="build/" />
|
||||||
<copy file="apps/phttprelay/java/build/phttprelay.war" todir="build/" />
|
<copy file="apps/phttprelay/java/build/phttprelay.war" todir="build/" />
|
||||||
<copy file="apps/sam/java/build/sam.jar" todir="build/" />
|
<copy file="apps/sam/java/build/sam.jar" todir="build/" />
|
||||||
|
Reference in New Issue
Block a user