diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java index 6d0a4faa9..1f433b60f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java @@ -35,6 +35,10 @@ public class ConfigClientsHandler extends FormHandler { saveWebAppChanges(); return; } + if (_action.equals(_("Save Plugin Configuration"))) { + savePluginChanges(); + return; + } if (_action.equals(_("Install Plugin"))) { installPlugin(); return; @@ -60,10 +64,42 @@ public class ConfigClientsHandler extends FormHandler { try { appnum = Integer.parseInt(app); } catch (NumberFormatException nfe) {} - if (appnum >= 0) + if (appnum >= 0) { deleteClient(appnum); + } else { + try { + PluginStarter.stopPlugin(_context, app); + } catch (Exception e) {} + PluginStarter.deletePlugin(_context, app); + addFormNotice(_("Deleted plugin {0}", app)); + } return; } + + // value + if (_action.startsWith("Stop ")) { + String app = _action.substring(5); + try { + PluginStarter.stopPlugin(_context, app); + } catch (Exception e) {} + addFormNotice(_("Stopped plugin {0}", app)); + return; + } + + // value + if (_action.startsWith("Update ")) { + String app = _action.substring(7); + updatePlugin(app); + return; + } + + // value + if (_action.startsWith("Check ")) { + String app = _action.substring(6); + checkPlugin(app); + return; + } + // label (IE) String xStart = _("Start"); if (_action.toLowerCase().startsWith(xStart + " ") && @@ -81,6 +117,7 @@ public class ConfigClientsHandler extends FormHandler { } else { addFormError(_("Unsupported") + ' ' + _action + '.'); } + } public void setSettings(Map settings) { _settings = new HashMap(settings); } @@ -175,7 +212,23 @@ public class ConfigClientsHandler extends FormHandler { props.setProperty(name, "" + (val != null)); } RouterConsoleRunner.storeWebAppProperties(props); - addFormNotice(_("WebApp configuration saved successfully - restart required to take effect.")); + addFormNotice(_("WebApp configuration saved.")); + } + + private void savePluginChanges() { + Properties props = PluginStarter.pluginProperties(); + Set keys = props.keySet(); + int cur = 0; + for (Iterator iter = keys.iterator(); iter.hasNext(); ) { + String name = (String)iter.next(); + if (! (name.startsWith(PluginStarter.PREFIX) && name.endsWith(PluginStarter.ENABLED))) + continue; + String app = name.substring(PluginStarter.PREFIX.length(), name.lastIndexOf(PluginStarter.ENABLED)); + Object val = _settings.get(app + ".enabled"); + props.setProperty(name, "" + (val != null)); + } + PluginStarter.storePluginProperties(props); + addFormNotice(_("Plugin configuration saved.")); } /** @@ -205,6 +258,20 @@ public class ConfigClientsHandler extends FormHandler { addFormError(_("No plugin URL specified.")); return; } + installPlugin(url); + } + + private void updatePlugin(String app) { + Properties props = PluginStarter.pluginProperties(_context, app); + String url = props.getProperty("updateURL"); + if (url == null) { + addFormError(_("No update URL specified for {0}",app)); + return; + } + installPlugin(url); + } + + private void installPlugin(String url) { if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) { addFormError(_("Plugin or update download already in progress.")); return; @@ -221,4 +288,23 @@ public class ConfigClientsHandler extends FormHandler { Thread.sleep(1000); } catch (InterruptedException ie) {} } + + private void checkPlugin(String app) { + if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) { + addFormError(_("Plugin or update download already in progress.")); + return; + } + PluginUpdateChecker puc = PluginUpdateChecker.getInstance(_context); + if (puc.isRunning()) { + addFormError(_("Plugin or update download already in progress.")); + return; + } + puc.update(app); + addFormNotice(_("Checking plugin {0} for updates", app)); + // So that update() will post a status to the summary bar before we reload + try { + Thread.sleep(1000); + } catch (InterruptedException ie) {} + } + } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java index 2f3877459..d0daac4b2 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java @@ -70,6 +70,10 @@ public class ConfigClientsHelper extends HelperBase { return buf.toString(); } + public boolean showPlugins() { + return PluginStarter.pluginsEnabled(_context); + } + public String getForm3() { StringBuilder buf = new StringBuilder(1024); buf.append("\n"); @@ -170,7 +174,7 @@ public class ConfigClientsHelper extends HelperBase { if (ro) buf.append("disabled=\"true\" "); } - buf.append("/>
"); + buf.append(">"); if ((!enabled) && !edit) { buf.append(""); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java index d24e28d00..5b92ec0c3 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -19,6 +19,7 @@ import net.i2p.data.DataHelper; import net.i2p.router.RouterContext; import net.i2p.router.startup.ClientAppConfig; import net.i2p.router.startup.LoadClientAppsJob; +import net.i2p.util.FileUtil; import net.i2p.util.Log; import net.i2p.util.Translate; @@ -41,6 +42,10 @@ public class PluginStarter implements Runnable { _context = ctx; } + static boolean pluginsEnabled(I2PAppContext ctx) { + return Boolean.valueOf(ctx.getProperty("router.enablePlugins")).booleanValue(); + } + public void run() { startPlugins(_context); } @@ -50,9 +55,9 @@ public class PluginStarter implements Runnable { Properties props = pluginProperties(); for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { String name = (String)iter.next(); - if (name.startsWith(PluginStarter.PREFIX) && name.endsWith(PluginStarter.ENABLED)) { + if (name.startsWith(PREFIX) && name.endsWith(ENABLED)) { if (Boolean.valueOf(props.getProperty(name)).booleanValue()) { - String app = name.substring(PluginStarter.PREFIX.length(), name.lastIndexOf(PluginStarter.ENABLED)); + String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED)); try { if (!startPlugin(ctx, app)) log.error("Failed to start plugin: " + app); @@ -188,7 +193,6 @@ public class PluginStarter implements Runnable { } } - // remove summary bar link Properties props = pluginProperties(ctx, appName); String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx)); @@ -200,6 +204,25 @@ public class PluginStarter implements Runnable { return true; } + /** @return true on success - call stopPlugin() first */ + static boolean deletePlugin(RouterContext ctx, String appName) { + Log log = ctx.logManager().getLog(PluginStarter.class); + File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName); + if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) { + log.error("Cannot stop nonexistent plugin: " + appName); + return false; + } + FileUtil.rmdir(pluginDir, false); + Properties props = pluginProperties(); + for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { + String name = (String)iter.next(); + if (name.startsWith(PREFIX + appName)) + iter.remove(); + } + storePluginProperties(props); + return true; + } + /** plugin.config */ public static Properties pluginProperties(I2PAppContext ctx, String appName) { File cfgFile = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName + '/' + "plugin.config"); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java index e19e23d91..8b5d2c014 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java @@ -62,14 +62,15 @@ public class PluginUpdateChecker extends UpdateHandler { String oldVersion = props.getProperty("version"); String xpi2pURL = props.getProperty("updateURL"); if (oldVersion == null || xpi2pURL == null) { - updateStatus("" + _("Cannot update, plugin {0} is not installed", appName) + ""); + updateStatus("" + _("Cannot check, plugin {0} is not installed", appName) + ""); return; } if (_pluginUpdateCheckerRunner == null) - _pluginUpdateCheckerRunner = new PluginUpdateCheckerRunner(xpi2pURL); + _pluginUpdateCheckerRunner = new PluginUpdateCheckerRunner(); if (_pluginUpdateCheckerRunner.isRunning()) return; + _xpi2pURL = xpi2pURL; _appName = appName; _oldVersion = oldVersion; System.setProperty(PROP_UPDATE_IN_PROGRESS, "true"); @@ -89,18 +90,16 @@ public class PluginUpdateChecker extends UpdateHandler { } public class PluginUpdateCheckerRunner extends UpdateRunner implements Runnable, EepGet.StatusListener { - String _updateURL; ByteArrayOutputStream _baos; - public PluginUpdateCheckerRunner(String url) { + public PluginUpdateCheckerRunner() { super(); - _updateURL = url; _baos = new ByteArrayOutputStream(TrustedUpdate.HEADER_BYTES); } @Override protected void update() { - updateStatus("" + _("Checking plugin {0} for updates", _appName) + ""); + updateStatus("" + _("Checking for update of plugin {0}", _appName) + ""); // use the same settings as for updater boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue(); String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java index 60df3f89e..8e0ba021f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java @@ -90,11 +90,9 @@ public class PluginUpdateHandler extends UpdateHandler { } public class PluginUpdateRunner extends UpdateRunner implements Runnable, EepGet.StatusListener { - String _updateURL; public PluginUpdateRunner(String url) { super(); - _updateURL = url; } @Override @@ -287,7 +285,7 @@ public class PluginUpdateHandler extends UpdateHandler { if (oldVersion == null || (new VersionComparator()).compare(oldVersion, version) >= 0) { to.delete(); - updateStatus("" + _("New plugin version {0} is not newer than installed plugin", version) + ""); + updateStatus("" + _("Downloaded plugin version {0} is not newer than installed plugin", version) + ""); return; } minVersion = ConfigClientsHelper.stripHTML(props, "min-installed-version"); 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 07d3c2ed7..f4f7bd79e 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -189,8 +189,10 @@ public class RouterConsoleRunner { List contexts = RouterContext.listContexts(); if (contexts != null) { - t = new I2PAppThread(new PluginStarter(contexts.get(0)), "PluginStarter", true); - t.start(); + if (PluginStarter.pluginsEnabled(contexts.get(0))) { + t = new I2PAppThread(new PluginStarter(contexts.get(0)), "PluginStarter", true); + t.start(); + } } } diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp index 6d52901b8..4bb17007c 100644 --- a/apps/routerconsole/jsp/configclients.jsp +++ b/apps/routerconsole/jsp/configclients.jsp @@ -54,16 +54,20 @@ button span.hide{ <%=intl._("All changes require restart to take effect.")%>


" /> -

<%=intl._("Plugin Configuration")%>

+ +<% if (clientshelper.showPlugins()) { %> +

<%=intl._("Plugin Configuration")%>

<%=intl._("The plugins listed below are started by the webConsole client.")%>


" />

<%=intl._("Plugin Installation")%>

- <%=intl._("To install a plugin, enter the URL to download the plugin from:")%> + <%=intl._("To install a plugin, enter the download URL:")%>


" /> -
+ +<% } %> +