From fc6f4ecc7430f4130eb9b5de7d011eba6f1a36fb Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Sun, 15 Jan 2012 21:15:08 +0000
Subject: [PATCH]   * Plugins:     - Auto-update plugins after a router update 
    - Add update-all button

---
 .../i2p/router/web/ConfigClientsHandler.java  | 14 +++
 .../src/net/i2p/router/web/PluginStarter.java | 92 +++++++++++++++++++
 .../i2p/router/web/PluginUpdateChecker.java   | 32 ++++++-
 .../i2p/router/web/PluginUpdateHandler.java   |  2 +-
 .../src/net/i2p/router/web/UpdateHandler.java |  2 +-
 apps/routerconsole/jsp/configclients.jsp      |  4 +
 6 files changed, 141 insertions(+), 5 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 eb28a4b90e..3e9a10db9b 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
@@ -53,6 +53,10 @@ public class ConfigClientsHandler extends FormHandler {
             installPlugin();
             return;
         }
+        if (_action.equals(_("Update All Installed Plugins"))) {
+            updateAllPlugins();
+            return;
+        }
         // value
         if (_action.startsWith("Start ")) {
             String app = _action.substring(6);
@@ -321,6 +325,16 @@ public class ConfigClientsHandler extends FormHandler {
         installPlugin(url);
     }
 
+    /** @since 0.8.13 */
+    private void updateAllPlugins() {
+        addFormNotice(_("Updating all plugins"));
+        PluginStarter.updateAll(_context);
+        // So that update() will post a status to the summary bar before we reload
+        try {
+           Thread.sleep(1000);
+        } catch (InterruptedException ie) {}
+    }
+
     private void installPlugin(String url) {
         if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) {
             addFormError(_("Plugin or update download already in progress."));
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 29f083bc92..c4c567b3f9 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
@@ -22,10 +22,12 @@ import net.i2p.I2PAppContext;
 import net.i2p.data.DataHelper;
 import net.i2p.router.Job;
 import net.i2p.router.RouterContext;
+import net.i2p.router.RouterVersion;
 import net.i2p.router.startup.ClientAppConfig;
 import net.i2p.router.startup.LoadClientAppsJob;
 import net.i2p.util.ConcurrentHashSet;
 import net.i2p.util.FileUtil;
+import net.i2p.util.I2PAppThread;
 import net.i2p.util.Log;
 import net.i2p.util.Translate;
 import net.i2p.util.VersionComparator;
@@ -63,9 +65,96 @@ public class PluginStarter implements Runnable {
     }
 
     public void run() {
+        if (_context.getBooleanPropertyDefaultTrue("plugins.autoUpdate") &&
+            (!Boolean.valueOf(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS)).booleanValue()) &&
+            (!RouterVersion.VERSION.equals(_context.getProperty("router.previousVersion"))))
+            updateAll(_context, true);
         startPlugins(_context);
     }
 
+    /**
+     *  threaded
+     *  @since 0.8.13
+     */
+    static void updateAll(RouterContext ctx) {
+        Thread t = new I2PAppThread(new PluginUpdater(ctx), "PluginUpdater", true);
+        t.start();
+    }
+
+    /**
+     *  thread
+     *  @since 0.8.13
+     */
+    private static class PluginUpdater implements Runnable {
+        private final RouterContext _ctx;
+
+        public PluginUpdater(RouterContext ctx) {
+            _ctx = ctx;
+        }
+
+        public void run() {
+            updateAll(_ctx, false);
+        }
+    }
+
+    /**
+     *  inline
+     *  @since 0.8.13
+     */
+    private static void updateAll(RouterContext ctx, boolean delay) {
+        List<String> plugins = getPlugins();
+        Map<String, String> toUpdate = new HashMap();
+        for (String appName : plugins) {
+            Properties props = pluginProperties(ctx, appName);
+            String url = props.getProperty("updateURL");
+            if (url != null)
+                toUpdate.put(appName, url);
+        }
+        if (toUpdate.isEmpty())
+            return;
+        PluginUpdateChecker puc = PluginUpdateChecker.getInstance(ctx);
+        if (puc.isRunning())
+            return;
+
+        if (delay) {
+            // wait for proxy
+            System.setProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS, "true");
+            puc.setAppStatus(Messages.getString("Checking for plugin updates", ctx));
+            try {
+                Thread.sleep(3*60*1000);
+            } catch (InterruptedException ie) {}
+            System.setProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS, "false");
+        }
+
+        Log log = ctx.logManager().getLog(PluginStarter.class);
+        for (Map.Entry<String, String> entry : toUpdate.entrySet()) {
+            String appName = entry.getKey();
+            if (log.shouldLog(Log.WARN))
+                log.warn("Checking for update plugin: " + appName);
+            puc.update(appName);
+            do {
+                try {
+                    Thread.sleep(5*1000);
+                } catch (InterruptedException ie) {}
+            } while (puc.isRunning());
+            if (!puc.isNewerAvailable()) {
+                if (log.shouldLog(Log.WARN))
+                    log.warn("No update available for plugin: " + appName);
+                continue;
+            }
+            PluginUpdateHandler puh = PluginUpdateHandler.getInstance(ctx);
+            String url = entry.getValue();
+            if (log.shouldLog(Log.WARN))
+                log.warn("Updating plugin: " + appName);
+            puh.update(url);
+            do {
+                try {
+                    Thread.sleep(5*1000);
+                } catch (InterruptedException ie) {}
+            } while (puh.isRunning());
+        }
+    }
+
     /** this shouldn't throw anything */
     static void startPlugins(RouterContext ctx) {
         Log log = ctx.logManager().getLog(PluginStarter.class);
@@ -75,6 +164,9 @@ public class PluginStarter implements Runnable {
             if (name.startsWith(PREFIX) && name.endsWith(ENABLED)) {
                 if (Boolean.valueOf(props.getProperty(name)).booleanValue()) {
                     String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED));
+                    // plugins could have been started after update
+                    if (isPluginRunning(app, ctx))
+                        continue;
                     try {
                         if (!startPlugin(ctx, app))
                             log.error("Failed to start plugin: " + app);
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 cf3f75f3eb..1e7fceb40f 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java
@@ -36,6 +36,7 @@ public class PluginUpdateChecker extends UpdateHandler {
     private String _appName;
     private String _oldVersion;
     private String _xpi2pURL;
+    private volatile boolean _isNewerAvailable;
 
     private static PluginUpdateChecker _instance;
     public static final synchronized PluginUpdateChecker getInstance(RouterContext ctx) { 
@@ -49,12 +50,19 @@ public class PluginUpdateChecker extends UpdateHandler {
         super(ctx);
     }
     
-    /** check all plugins */
+    /**
+     *  check all plugins
+     *  @deprecated not finished
+     */
     public void update() {
         Thread t = new I2PAppThread(new AllCheckerRunner(), "AllAppChecker", true);
         t.start();
     }
 
+    /**
+     *  check all plugins
+     *  @deprecated not finished
+     */
     public class AllCheckerRunner implements Runnable {
         public void run() {
             List<String> plugins = PluginStarter.getPlugins();
@@ -85,12 +93,18 @@ public class PluginUpdateChecker extends UpdateHandler {
             _xpi2pURL = xpi2pURL;
             _appName = appName;
             _oldVersion = oldVersion;
+            _isNewerAvailable = false;
             System.setProperty(PROP_UPDATE_IN_PROGRESS, "true");
             I2PAppThread update = new I2PAppThread(_pluginUpdateCheckerRunner, "AppChecker", true);
             update.start();
         }
     }
     
+    /** @since 0.8.13 */
+    public void setAppStatus(String status) {
+        updateStatus(status);
+    }
+
     public boolean isRunning() {
         return _pluginUpdateCheckerRunner != null && _pluginUpdateCheckerRunner.isRunning();
     }
@@ -101,6 +115,11 @@ public class PluginUpdateChecker extends UpdateHandler {
         return false;
     }
     
+    /** @since 0.8.13 */
+    public boolean isNewerAvailable() {
+        return _isNewerAvailable;
+    }
+
     private void scheduleStatusClean(String msg) {
         SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000);
     }
@@ -126,6 +145,7 @@ public class PluginUpdateChecker extends UpdateHandler {
 
         @Override
         protected void update() {
+            _isNewerAvailable = false;
             updateStatus("<b>" + _("Checking for update of plugin {0}", _appName) + "</b>");
             // use the same settings as for updater
             // always proxy, or else FIXME
@@ -142,6 +162,10 @@ public class PluginUpdateChecker extends UpdateHandler {
             }
         }
         
+        public boolean isNewerAvailable() {
+            return _isNewerAvailable;
+        }
+
         @Override
         public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
         }
@@ -151,10 +175,12 @@ public class PluginUpdateChecker extends UpdateHandler {
             String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray()));
             boolean newer = (new VersionComparator()).compare(newVersion, _oldVersion) > 0;
             String msg;
-            if (newer)
+            if (newer) {
                 msg = "<b>" + _("New plugin version {0} is available", newVersion) + "</b>";
-            else
+                _isNewerAvailable = true;
+            } else {
                 msg = "<b>" + _("No new version is available for plugin {0}", _appName) + "</b>";
+            }
             updateStatus(msg);
             scheduleStatusClean(msg);
         }
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 e2499d1312..df7f19a4cd 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java
@@ -93,7 +93,7 @@ public class PluginUpdateHandler extends UpdateHandler {
     }
     
     private void scheduleStatusClean(String msg) {
-        SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000);
+        SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 20*60*1000);
     }
 
     private class Cleaner implements SimpleTimer.TimedEvent {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
index 2f2d3db74f..5b7eaec03c 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
@@ -127,7 +127,7 @@ public class UpdateHandler {
     }
     
     public class UpdateRunner implements Runnable, EepGet.StatusListener {
-        protected boolean _isRunning;
+        protected volatile boolean _isRunning;
         protected boolean done;
         protected EepGet _get;
         protected final DecimalFormat _pct = new DecimalFormat("0.0%");
diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp
index 4c70beafed..25df053f46 100644
--- a/apps/routerconsole/jsp/configclients.jsp
+++ b/apps/routerconsole/jsp/configclients.jsp
@@ -103,6 +103,7 @@ button span.hide{
  <jsp:getProperty name="clientshelper" property="form2" />
  <p><i><%=intl._("All changes require restart to take effect.")%></i>
  </p><hr><div class="formaction">
+ <input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
  <input type="submit" name="action" class="accept" value="<%=intl._("Save WebApp Configuration")%>" />
 </div></form></div>
 
@@ -114,6 +115,7 @@ button span.hide{
 <input type="hidden" name="nonce" value="<%=pageNonce%>" >
  <jsp:getProperty name="clientshelper" property="form3" />
 <hr><div class="formaction">
+ <input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
  <input type="submit" name="action" class="accept" value="<%=intl._("Save Plugin Configuration")%>" />
 </div></form></div>
 
@@ -125,7 +127,9 @@ button span.hide{
 <p>
  <input type="text" size="60" name="pluginURL" >
  </p><hr><div class="formaction">
+ <input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
  <input type="submit" name="action" class="download" value="<%=intl._("Install Plugin")%>" />
+ <input type="submit" name="action" class="reload" value="<%=intl._("Update All Installed Plugins")%>" />
  </div></form></div>
 <% } %>
 </div></div></body></html>
-- 
GitLab