diff --git a/apps/i2pcontrol/build.xml b/apps/i2pcontrol/build.xml index e6cf773070..053887c8b4 100644 --- a/apps/i2pcontrol/build.xml +++ b/apps/i2pcontrol/build.xml @@ -51,7 +51,7 @@ - + diff --git a/apps/i2pcontrol/java/net/i2p/i2pcontrol/HostCheckHandler.java b/apps/i2pcontrol/java/net/i2p/i2pcontrol/HostCheckHandler.java index 455547dd0d..7b8587ddc3 100644 --- a/apps/i2pcontrol/java/net/i2p/i2pcontrol/HostCheckHandler.java +++ b/apps/i2pcontrol/java/net/i2p/i2pcontrol/HostCheckHandler.java @@ -24,6 +24,8 @@ import org.eclipse.jetty.server.handler.HandlerWrapper; * all the webapps (not just routerconsole). * Therefore, this protects all the webapps. * + * This class is NOT used for the webapp or the bare ServerSocket implementation. + * * @since 0.12 copied from routerconsole */ public class HostCheckHandler extends HandlerWrapper diff --git a/apps/i2pcontrol/java/net/i2p/i2pcontrol/I2PControlController.java b/apps/i2pcontrol/java/net/i2p/i2pcontrol/I2PControlController.java index a70f449e63..bf519031bf 100644 --- a/apps/i2pcontrol/java/net/i2p/i2pcontrol/I2PControlController.java +++ b/apps/i2pcontrol/java/net/i2p/i2pcontrol/I2PControlController.java @@ -59,6 +59,8 @@ import java.util.StringTokenizer; * * Usage: I2PControlController -d $PLUGIN [start|stop] * + * This class is NOT used for the webapp or the bare ServerSocket implementation. + * * @author hottuna */ public class I2PControlController implements RouterApp { @@ -78,6 +80,7 @@ public class I2PControlController implements RouterApp { private static I2PControlController _instance; static final String PROP_ALLOWED_HOSTS = "i2pcontrol.allowedhosts"; private static final String SVC_HTTPS_I2PCONTROL = "https_i2pcontrol"; + private static final int DEFAULT_PORT = 7650; /** * RouterApp (new way) @@ -207,7 +210,7 @@ public class I2PControlController implements RouterApp { _server.start(); _context.portMapper().register(SVC_HTTPS_I2PCONTROL, _conf.getConf("i2pcontrol.listen.address", "127.0.0.1"), - _conf.getConf("i2pcontrol.listen.port", 7650)); + _conf.getConf("i2pcontrol.listen.port", DEFAULT_PORT)); } @@ -218,7 +221,7 @@ public class I2PControlController implements RouterApp { */ private Connector buildDefaultListener(Server server) { Connector ssl = buildSslListener(server, _conf.getConf("i2pcontrol.listen.address", "127.0.0.1"), - _conf.getConf("i2pcontrol.listen.port", 7650)); + _conf.getConf("i2pcontrol.listen.port", DEFAULT_PORT)); return ssl; } diff --git a/apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java b/apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java new file mode 100644 index 0000000000..b2e5ca5d07 --- /dev/null +++ b/apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java @@ -0,0 +1,246 @@ +package net.i2p.i2pcontrol; +/* + * Copyright 2010 hottuna (dev@robertfoss.se) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.List; + +import net.i2p.I2PAppContext; +import net.i2p.app.ClientAppManager; +import net.i2p.app.ClientAppState; +import static net.i2p.app.ClientAppState.*; +import net.i2p.router.RouterContext; +import net.i2p.router.app.RouterApp; +import net.i2p.util.I2PSSLSocketFactory; +import net.i2p.util.Log; +import net.i2p.util.PortMapper; + +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import net.i2p.i2pcontrol.security.KeyStoreProvider; +import net.i2p.i2pcontrol.security.SecurityManager; +import net.i2p.i2pcontrol.servlets.JSONRPC2Servlet; +import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager; + + +/** + * This handles the starting and stopping of a ServerSocket + * from a single static class so it can be called via clients.config. + * + * This class is NOT used for the webapp or the HTTP/HTTPS implementation. + * + * @author hottuna + */ +public class SocketController implements RouterApp { + // non-null + private final RouterContext _context; + private final ClientAppManager _mgr; + private final Log _log; + private final String _pluginDir; + private final ConfigurationManager _conf; + private final KeyStoreProvider _ksp; + private final SecurityManager _secMan; + private ServerSocket _server; + private final List _listeners; + private ClientAppState _state = UNINITIALIZED; + // only for main() + private static SocketController _instance; + static final String PROP_ALLOWED_HOSTS = "i2pcontrol.allowedhosts"; + private static final String SVC_SKT_I2PCONTROL = "skt_i2pcontrol"; + private static final String SVC_SSL_I2PCONTROL = "skt_ssl_i2pcontrol"; + private static final int DEFAULT_PORT = 7640; + + /** + * RouterApp (new way) + */ + public SocketController(RouterContext ctx, ClientAppManager mgr, String args[]) throws IOException { + _context = ctx; + _mgr = mgr; + _log = _context.logManager().getLog(SocketController.class); + File pluginDir = new File(_context.getConfigDir(), "keystore/I2PControl"); + _pluginDir = pluginDir.getAbsolutePath(); + _conf = new ConfigurationManager(_context, pluginDir, true); + _ksp = new KeyStoreProvider(_pluginDir); + _secMan = new SecurityManager(_context, _ksp, _conf); + _listeners = new ArrayList(4); + _state = INITIALIZED; + } + + /////// ClientApp methods + + public synchronized void startup() { + changeState(STARTING); + try { + start(null); + changeState(RUNNING); + } catch (Exception e) { + changeState(START_FAILED, "Failed to start", e); + _log.error("Unable to start socket server", e); + stop(); + } + } + + public synchronized void shutdown(String[] args) { + if (_state == STOPPED) + return; + changeState(STOPPING); + stop(); + changeState(STOPPED); + } + + public synchronized ClientAppState getState() { + return _state; + } + + public String getName() { + return "I2PControl-Socket"; + } + + public String getDisplayName() { + return "I2PControl-Socket"; + } + + /////// end ClientApp methods + + private void changeState(ClientAppState state) { + changeState(state, null, null); + } + + private synchronized void changeState(ClientAppState state, String msg, Exception e) { + _state = state; + if (_mgr != null) + _mgr.notify(this, state, msg, e); + if (_context == null) { + if (msg != null) + System.out.println(state + ": " + msg); + if (e != null) + e.printStackTrace(); + } + } + + private synchronized void start(String args[]) throws Exception { + _context.logManager().getLog(JSONRPC2Servlet.class).setMinimumPriority(Log.DEBUG); + _server = buildServer(); + _context.portMapper().register(SVC_SKT_I2PCONTROL, + _conf.getConf("i2pcontrol.listen.address", "127.0.0.1"), + _conf.getConf("i2pcontrol.listen.port", DEFAULT_PORT)); + } + + /** + * Builds a new server. Used for changing ports during operation and such. + * + * Does NOT start the server. Must call start() on the returned server. + * + * @return Server - A new server built from current configuration. + */ + public ServerSocket buildServer() throws IOException { + String address = _conf.getConf("i2pcontrol.listen.address", "127.0.0.1"); + int port = _conf.getConf("i2pcontrol.listen.port", DEFAULT_PORT); + ServerSocket server = new ServerSocket(port, 0, InetAddress.getByName(address)); + _conf.writeConfFile(); + return server; + } + + private class Server implements Runnable { + public void run() { + try { + while (true) { + Socket s = _server.accept(); + synchronized (SocketController.this) { + _listeners.add(s); + } + new Handler(s); + } + } catch (IOException ioe) { + _log.error("i2pcontrol server", ioe); + } finally { + synchronized (SocketController.this) { + if (_server != null) { + try { _server.close(); } catch (IOException ioe) {} + } + } + } + } + } + + private class Handler implements Runnable { + private final Socket s; + + public Handler(Socket skt) { s = skt; } + + public void run() { + try { + final BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8")); + final JSONParser parser = new JSONParser(); + while (true) { + Object o = parser.parse(reader); + // TODO + System.out.println("i2pcontrol got: " + o); + } + } catch (ParseException pe) { + _log.error("i2pcontrol handler", pe); + return; + } catch (IOException ioe) { + _log.error("i2pcontrol handler", ioe); + return; + } finally { + synchronized (SocketController.this) { + _listeners.remove(s); + } + try { s.close(); } catch (IOException ioe) {} + } + } + } + + /** + * Stop it + */ + private synchronized void stopServer() + { + try { + if (_server != null) { + _context.portMapper().unregister(SVC_SKT_I2PCONTROL); + try { + _server.close(); + } catch (IOException ioe) {} + for (Socket listener : _listeners) { + try { + listener.close(); + } catch (IOException ioe) {} + } + _listeners.clear(); + } + } catch (Exception e) { + _log.error("Stopping server", e); + } + } + + private synchronized void stop() { + _conf.writeConfFile(); + _secMan.stopTimedEvents(); + stopServer(); + } +} diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java index 69ee850d27..5fcd47b89f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -802,6 +802,10 @@ public class RouterConsoleRunner implements RouterApp { props.remove(PREFIX + "addressbook" + ENABLED); rewrite = true; } + } else if (appName.equals("jsonrpc") && enabled == null) { + // jsonrpc (i2pcontrol) webapp default is false + props.setProperty(PREFIX + "jsonrpc" + ENABLED, "false"); + rewrite = true; } else if (! "false".equals(enabled)) { try { String path = files[i].getCanonicalPath(); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryBarRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryBarRenderer.java index 5e6b8595ec..36b7e90210 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryBarRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryBarRenderer.java @@ -228,6 +228,14 @@ class SummaryBarRenderer { .append("\n"); } + if (pm.isRegistered(PortMapper.SVC_JSONRPC)) { + buf.append("") + .append(nbsp(_t("I2PControl"))) + .append("\n"); + } + if (pm.isRegistered(PortMapper.SVC_I2PSNARK)) { buf.append("