From acdaa60de32e925dcc84cf36326c60d3a7b8f1a8 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Wed, 3 Feb 2016 13:20:22 +0000 Subject: [PATCH] Console: Custom icons for non-webapp plugins, from cacapo (ticket #1550) --- .../router/web/CodedIconRendererServlet.java | 90 +++++++++++++++++++ .../src/net/i2p/router/web/NavHelper.java | 15 ++++ .../src/net/i2p/router/web/PluginStarter.java | 15 ++++ apps/routerconsole/jsp/web.xml | 12 +++ 4 files changed, 132 insertions(+) create mode 100644 apps/routerconsole/java/src/net/i2p/router/web/CodedIconRendererServlet.java diff --git a/apps/routerconsole/java/src/net/i2p/router/web/CodedIconRendererServlet.java b/apps/routerconsole/java/src/net/i2p/router/web/CodedIconRendererServlet.java new file mode 100644 index 0000000000..4745d945b8 --- /dev/null +++ b/apps/routerconsole/java/src/net/i2p/router/web/CodedIconRendererServlet.java @@ -0,0 +1,90 @@ +package net.i2p.router.web; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.File; +import java.util.Arrays; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.GenericServlet; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import net.i2p.data.Base64; + + + /** + * + * @author cacapo + */ + +public class CodedIconRendererServlet extends HttpServlet { + + public static final long serialVersionUID = 16851750L; + + String base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath(); + String file = "docs" + java.io.File.separatorChar + "themes" + java.io.File.separatorChar + "console" + java.io.File.separatorChar + "images" + java.io.File.separatorChar + "plugin.png"; + + + + @Override + + protected void service(HttpServletRequest srq, HttpServletResponse srs) throws ServletException, IOException { + byte[] data; + String name = srq.getParameter("plugin"); + data = NavHelper.getBinary(name); + + //set as many headers as are common to any outcome + + srs.setContentType("image/png"); + srs.setDateHeader("Expires", net.i2p.I2PAppContext.getGlobalContext().clock().now() + 86400000l); + srs.setHeader("Cache-Control", "public, max-age=86400"); + OutputStream os = srs.getOutputStream(); + + //Binary data is present + if(data != null){ + srs.setHeader("Content-Length", Integer.toString(data.length)); + int content = Arrays.hashCode(data); + int chksum = srq.getIntHeader("If-None-Match");//returns -1 if no such header + //Don't render if icon already present + if(content != chksum){ + srs.setIntHeader("ETag", content); + try{ + os.write(data); + os.flush(); + os.close(); + }catch(IOException e){ + net.i2p.I2PAppContext.getGlobalContext().logManager().getLog(getClass()).warn("Error writing binary image data for plugin", e); + } + } else { + srs.sendError(304, "Not Modified"); + } + } else { + //Binary data is not present but must be substituted by file on disk + File pfile = new java.io.File(base, file); + srs.setHeader("Content-Length", Long.toString(pfile.length())); + try{ + long lastmod = pfile.lastModified(); + if(lastmod > 0){ + long iflast = srq.getDateHeader("If-Modified-Since"); + if(iflast >= ((lastmod/1000) * 1000)){ + srs.sendError(304, "Not Modified"); + } else { + srs.setDateHeader("Last-Modified", lastmod); + net.i2p.util.FileUtil.readFile(file, base, os); + } + + } + } catch(java.io.IOException e) { + if (!srs.isCommitted()) { + srs.sendError(403, e.toString()); + } else { + net.i2p.I2PAppContext.getGlobalContext().logManager().getLog(getClass()).warn("Error serving plugin.png", e); + throw e; + } + } + + } + } +} diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NavHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/NavHelper.java index 713520c94a..02e397df1a 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NavHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NavHelper.java @@ -13,6 +13,7 @@ public class NavHelper { private static final Map<String, String> _apps = new ConcurrentHashMap<String, String>(4); private static final Map<String, String> _tooltips = new ConcurrentHashMap<String, String>(4); private static final Map<String, String> _icons = new ConcurrentHashMap<String, String>(4); + private static final Map<String, byte[]> _binary = new ConcurrentHashMap<String, byte[]>(4); /** * To register a new client application so that it shows up on the router @@ -40,6 +41,20 @@ public class NavHelper { _icons.remove(name); } + + public static byte[] getBinary(String name){ + if(name != null) + return _binary.get(name); + else + return null; + } + + + public static void setBinary(String name, byte[] arr){ + _binary.put(name, arr); + } + + /** * Translated string is loaded by PluginStarter * @param ctx unused 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 2af0d5df46..62cced0694 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -22,6 +22,7 @@ import net.i2p.I2PAppContext; import net.i2p.app.ClientApp; import net.i2p.app.ClientAppState; import net.i2p.data.DataHelper; +import net.i2p.data.Base64; import net.i2p.router.RouterContext; import net.i2p.router.RouterVersion; import net.i2p.router.startup.ClientAppConfig; @@ -353,6 +354,20 @@ public class PluginStarter implements Runnable { } } + + //handle console icons for plugins without web-resources through prop icon-code + String fullprop = props.getProperty("icon-code"); + if(fullprop != null && fullprop.length() > 1){ + byte[] decoded = Base64.decode(fullprop); + if(decoded != null) { + NavHelper.setBinary(appName, decoded); + iconfile = "/Plugins/pluginicon?plugin=" + appName; + } else { + iconfile = "/themes/console/images/plugin.png"; + } + } + + // load and start things in clients.config File clientConfig = new File(pluginDir, "clients.config"); if (clientConfig.exists()) { diff --git a/apps/routerconsole/jsp/web.xml b/apps/routerconsole/jsp/web.xml index ea183c8356..44e40c86e5 100644 --- a/apps/routerconsole/jsp/web.xml +++ b/apps/routerconsole/jsp/web.xml @@ -14,6 +14,18 @@ </filter-mapping> <!-- precompiled servlets --> + + <servlet> + <servlet-name>net.i2p.router.web.CodedIconRendererServlet</servlet-name> + <servlet-class>net.i2p.router.web.CodedIconRendererServlet</servlet-class> + </servlet> + + <servlet-mapping> + <servlet-name>net.i2p.router.web.CodedIconRendererServlet</servlet-name> + <url-pattern>/Plugins/*</url-pattern> + </servlet-mapping> + + <!-- yeah, i'm lazy, using a jsp instead of a servlet.. --> <servlet-mapping> -- GitLab