From 949aea951e4fada832750a16ee26f28c2d6f2f41 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Wed, 10 Feb 2010 19:09:35 +0000
Subject: [PATCH]     * Plugins:       - Hook up update/delete/check/save
 buttons       - Implement delete       - Hide unless
 router.enablePlugins=true

---
 .../i2p/router/web/ConfigClientsHandler.java  | 90 ++++++++++++++++++-
 .../i2p/router/web/ConfigClientsHelper.java   |  6 +-
 .../src/net/i2p/router/web/PluginStarter.java | 29 +++++-
 .../i2p/router/web/PluginUpdateChecker.java   | 11 ++-
 .../i2p/router/web/PluginUpdateHandler.java   |  4 +-
 .../i2p/router/web/RouterConsoleRunner.java   |  6 +-
 apps/routerconsole/jsp/configclients.jsp      | 10 ++-
 7 files changed, 136 insertions(+), 20 deletions(-)

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 6d0a4faa9f..1f433b60f4 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 + "<span class=hide> ") &&
@@ -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 2f38774591..d0daac4b2e 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("<table>\n");
@@ -170,7 +174,7 @@ public class ConfigClientsHelper extends HelperBase {
             if (ro)
                 buf.append("disabled=\"true\" ");
         }
-        buf.append("/></td><td align=\"center\" width=\"15%\">");
+        buf.append("></td><td align=\"center\" width=\"15%\">");
         if ((!enabled) && !edit) {
             buf.append("<button type=\"submit\" name=\"action\" value=\"Start ").append(index).append("\" >" + _("Start") + "<span class=hide> ").append(index).append("</span></button>");
         }
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 d24e28d00d..5b92ec0c3b 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 e19e23d91f..8b5d2c0147 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("<b>" + _("Cannot update, plugin {0} is not installed", appName) + "</b>");
+                updateStatus("<b>" + _("Cannot check, plugin {0} is not installed", appName) + "</b>");
                 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("<b>" + _("Checking plugin {0} for updates", _appName) + "</b>");
+            updateStatus("<b>" + _("Checking for update of plugin {0}", _appName) + "</b>");
             // 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 60df3f89ec..8e0ba021f6 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("<b>" + _("New plugin version {0} is not newer than installed plugin", version) + "</b>");
+                    updateStatus("<b>" + _("Downloaded plugin version {0} is not newer than installed plugin", version) + "</b>");
                     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 07d3c2ed75..f4f7bd79e0 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<RouterContext> 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 6d52901b8b..4bb17007c6 100644
--- a/apps/routerconsole/jsp/configclients.jsp
+++ b/apps/routerconsole/jsp/configclients.jsp
@@ -54,16 +54,20 @@ button span.hide{
  <i><%=intl._("All changes require restart to take effect.")%></i>
  </p><hr><div class="formaction">
  <input type="submit" name="action" value="<%=intl._("Save WebApp Configuration")%>" />
-</div></div><h3><a name="webapp"></a><%=intl._("Plugin Configuration")%></h3><p>
+</div></div>
+<% if (clientshelper.showPlugins()) { %>
+<h3><a name="webapp"></a><%=intl._("Plugin Configuration")%></h3><p>
  <%=intl._("The plugins listed below are started by the webConsole client.")%>
  </p><div class="wideload"><p>
  <jsp:getProperty name="clientshelper" property="form3" />
  </p><hr><div class="formaction">
  <input type="submit" name="action" value="<%=intl._("Save Plugin Configuration")%>" />
 </div></div><h3><a name="plugin"></a><%=intl._("Plugin Installation")%></h3><p>
- <%=intl._("To install a plugin, enter the URL to download the plugin from:")%>
+ <%=intl._("To install a plugin, enter the download URL:")%>
  </p><div class="wideload"><p>
  <input type="text" size="60" name="pluginURL" >
  </p><hr><div class="formaction">
  <input type="submit" name="action" value="<%=intl._("Install Plugin")%>" />
- </div></div></form></div></div></body></html>
+ </div></div>
+<% } %>
+</form></div></div></body></html>
-- 
GitLab