diff --git a/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java
new file mode 100644
index 000000000..6c95734f7
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java
@@ -0,0 +1,182 @@
+package net.i2p.router.web;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.ClassLoader;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import net.i2p.crypto.SHA256Generator;
+import net.i2p.data.DataHelper;
+
+/**
+ * Dump info on jars and wars
+ *
+ * @since 0.8.13
+ */
+public class FileDumpHelper extends HelperBase {
+
+ public String getFileSummary() {
+ StringBuilder buf = new StringBuilder(16*1024);
+ buf.append("
").append(f.getAbsolutePath()).append(" | " +
+ "").append(f.length()).append(" | " +
+ "");
+ long mod = f.lastModified();
+ if (mod > 0)
+ buf.append((new Date(mod)).toString());
+ else
+ buf.append("Not found");
+ buf.append(" | ");
+ byte[] hash = sha256(f);
+ if (hash != null) {
+ byte[] hh = new byte[16];
+ System.arraycopy(hash, 0, hh, 0, 16);
+ buf.append("");
+ String p1 = DataHelper.toHexString(hh);
+ for (int i = p1.length(); i < 32; i++) {
+ buf.append('0');
+ }
+ buf.append(p1).append(" ");
+ System.arraycopy(hash, 16, hh, 0, 16);
+ buf.append("").append(DataHelper.toHexString(hh)).append("");
+ }
+ Attributes att = attributes(f);
+ if (att == null)
+ att = new Attributes();
+ buf.append(" | ");
+ String s = getAtt(att, "Base-Revision");
+ if (s != null && s.length() > 20) {
+ buf.append("" +
+ "").append(s.substring(0, 20)).append("" +
+ " " +
+ "").append(s.substring(20)).append("");
+ } else {
+ s = getAtt(att, "Implementation-Version");
+ if (s != null)
+ buf.append("").append(s).append("");
+ }
+ buf.append(" | ");
+ s = getAtt(att, "Created-By");
+ if (s != null)
+ buf.append(s);
+ buf.append(" | ");
+ s = getAtt(att, "Build-Date");
+ if (s != null)
+ buf.append(s);
+ buf.append(" | ");
+ s = getAtt(att, "Workspace-Changes");
+ if (s != null)
+ buf.append(s.replace(",", " "));
+ buf.append(" | ");
+ }
+
+ private static byte[] sha256(File f) {
+ InputStream in = null;
+ try {
+ in = new FileInputStream(f);
+ MessageDigest md = SHA256Generator.getDigestInstance();
+ byte[] b = new byte[4096];
+ int cnt = 0;
+ while ((cnt = in.read(b)) >= 0) {
+ md.update(b, 0, cnt);
+ }
+ return md.digest();
+ } catch (IOException ioe) {
+ //ioe.printStackTrace();
+ return null;
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException e) {}
+ }
+ }
+
+ private static Attributes attributes(File f) {
+ InputStream in = null;
+ try {
+ in = (new URL("jar:file:" + f.getAbsolutePath() + "!/META-INF/MANIFEST.MF")).openStream();
+ Manifest man = new Manifest(in);
+ return man.getMainAttributes();
+ } catch (IOException ioe) {
+ //ioe.printStackTrace();
+ return null;
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException e) {}
+ }
+ }
+
+ private static String getAtt(Attributes atts, String s) {
+ String rv = atts.getValue(s);
+ if (rv != null)
+ rv = DataHelper.stripHTML(rv);
+ return rv;
+ }
+}
diff --git a/apps/routerconsole/jsp/jars.jsp b/apps/routerconsole/jsp/jars.jsp
new file mode 100644
index 000000000..4fa200b70
--- /dev/null
+++ b/apps/routerconsole/jsp/jars.jsp
@@ -0,0 +1,14 @@
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+
+
+<%@include file="css.jsi" %>
+<%=intl.title("Jar File Dump")%>
+
+<%@include file="summary.jsi" %>Jar File Dump
+
+
+" />
+
+
diff --git a/history.txt b/history.txt
index 60c210c2a..0ca887885 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,16 @@
+2012-01-14 zzz
+ * i2ptunnel: Partial fix for dest formatting (ticket #581)
+ * jars.jsp: New debug page
+ * logs.jsp: Use wrapper method to find wrapper log if available
+ * Stats:
+ - Cleanups
+ - Remove some locking
+ - Change some longs to ints to save space
+ - Remove static logs
+
+2012-01-13 zzz
+ * i2prouter: Add translation infrastructure
+
2012-01-10 zzz
* Console:
- Add info to error 500 page
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 282c18b42..0725033fa 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
- public final static long BUILD = 2;
+ public final static long BUILD = 3;
/** for example "-test" */
public final static String EXTRA = "";