From d9979b8f975e5b27336cce761dab5e4e22d6db93 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 26 Dec 2022 05:38:35 -0500 Subject: [PATCH] Console: Sort tunnels by name on /tunnels, /configtunnels, /i2ptunnel --- .../src/net/i2p/i2ptunnel/web/IndexBean.java | 38 ++++++++++ apps/i2ptunnel/jsp/index.jsp | 6 +- .../web/helpers/ConfigTunnelsHelper.java | 73 +++++++++++++----- .../router/web/helpers/TunnelRenderer.java | 74 ++++++++++++------- 4 files changed, 144 insertions(+), 47 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index 6a6859d54..67bb66efe 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -10,7 +10,10 @@ package net.i2p.i2ptunnel.web; import java.io.File; import java.io.IOException; +import java.text.Collator; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -358,6 +361,41 @@ public class IndexBean { if (_group == null) return 0; return _group.getControllers().size(); } + + /** + * Return tunnel numbers of clients or servers only, sorted by tunnel name + * @param isClient true for clients, false for servers + * @return non-null, may be empty + * @since 0.9.57 + */ + public List getControllerNumbers(boolean isClient) { + if (_group == null) + return Collections.emptyList(); + List all = _group.getControllers(); + List rv = new ArrayList(all.size()); + for (int i = 0; i < all.size(); i++) { + TunnelController tc = all.get(i); + if (tc.isClient() == isClient) + rv.add(Integer.valueOf(i)); + } + if (rv.size() > 1) + Collections.sort(rv, new TCComparator()); + return rv; + } + + /** + * Sort tunnel numbers by the name of the tunnel + * @since 0.9.57 + */ + private class TCComparator implements Comparator { + private final Collator _comp = Collator.getInstance(); + public int compare(Integer l, Integer r) { + int rv = _comp.compare(getTunnelName(l), getTunnelName(r)); + if (rv != 0) + return rv; + return l.compareTo(r); + } + } /** * Is it a client or server in the UI and I2P side? diff --git a/apps/i2ptunnel/jsp/index.jsp b/apps/i2ptunnel/jsp/index.jsp index 95c156428..826de78ab 100644 --- a/apps/i2ptunnel/jsp/index.jsp +++ b/apps/i2ptunnel/jsp/index.jsp @@ -85,8 +85,7 @@ <%=intl._t("Control")%> <% - for (int curServer = 0; curServer < indexBean.getTunnelCount(); curServer++) { - if (indexBean.isClient(curServer)) continue; + for (int curServer : indexBean.getControllerNumbers(false)) { %> @@ -258,8 +257,7 @@ <%=intl._t("Control")%> <% - for (int curClient = 0; curClient < indexBean.getTunnelCount(); curClient++) { - if (!indexBean.isClient(curClient)) continue; + for (int curClient : indexBean.getControllerNumbers(true)) { %> diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigTunnelsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigTunnelsHelper.java index bb3ce71a6..539553117 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigTunnelsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigTunnelsHelper.java @@ -1,10 +1,15 @@ package net.i2p.router.web.helpers; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import java.util.Properties; import java.util.Set; import net.i2p.data.DataHelper; import net.i2p.data.Destination; +import net.i2p.data.Hash; import net.i2p.router.TunnelManagerFacade; import net.i2p.router.TunnelPoolSettings; import net.i2p.router.transport.TransportUtil; @@ -23,46 +28,78 @@ public class ConfigTunnelsHelper extends HelperBase { buf.append("\n"); int cur = 1; Set clients = _context.clientManager().listClients(); + TunnelManagerFacade mgr = _context.tunnelManager(); + // display name to in pool + List sorted = new ArrayList(clients.size()); for (Destination dest : clients) { + TunnelPoolSettings in = mgr.getInboundSettings(dest.calculateHash()); + if (in != null) + sorted.add(in); + } + if (sorted.size() > 1) + DataHelper.sort(sorted, new TPComparator()); + for (TunnelPoolSettings in : sorted) { buf.append("\n"); + buf.append(in.getDestination().toBase64()).append("\" >\n"); cur++; } buf.append("\n"); - TunnelManagerFacade mgr = _context.tunnelManager(); TunnelPoolSettings exploratoryIn = mgr.getInboundSettings(); TunnelPoolSettings exploratoryOut = mgr.getOutboundSettings(); renderForm(buf, 0, "exploratory", _t("Exploratory tunnels"), exploratoryIn, exploratoryOut); cur = 1; - for (Destination dest : clients) { - TunnelPoolSettings in = mgr.getInboundSettings(dest.calculateHash()); - TunnelPoolSettings out = mgr.getOutboundSettings(dest.calculateHash()); - - if (in == null || in.getAliasOf() != null || + for (TunnelPoolSettings in : sorted) { + Hash h = in.getDestination(); + TunnelPoolSettings out = mgr.getOutboundSettings(h); + + if (in.getAliasOf() != null || out == null || out.getAliasOf() != null) { cur++; continue; } - - String name = in.getDestinationNickname(); - if (name == null) { - name = out.getDestinationNickname(); - if (name == null) - name = dest.calculateHash().toBase32(); - } - - String prefix = dest.calculateHash().toBase64().substring(0,4); - renderForm(buf, cur, prefix, _t("Client tunnels for {0}", DataHelper.escapeHTML(_t(name))), in, out); + + String prefix = h.toBase64().substring(0,4); + renderForm(buf, cur, prefix, _t("Client tunnels for {0}", getTunnelName(in)), in, out); cur++; } - + buf.append("
\n"); return buf.toString(); } + /** + * Sort tunnels by the name of the tunnel + * @since 0.9.57 + */ + private class TPComparator implements Comparator { + private final Collator _comp = Collator.getInstance(); + public int compare(TunnelPoolSettings l, TunnelPoolSettings r) { + int rv = _comp.compare(getTunnelName(l), getTunnelName(r)); + if (rv != 0) + return rv; + return l.getDestination().toBase32().compareTo(r.getDestination().toBase32()); + } + } + + /** + * Get display name for the tunnel + * @since 0.9.57 + */ + private String getTunnelName(TunnelPoolSettings ins) { + String name = ins.getDestinationNickname(); + if (name == null) { + TunnelPoolSettings outPool = _context.tunnelManager().getOutboundSettings(ins.getDestination()); + if (outPool != null) + name = outPool.getDestinationNickname(); + } + if (name != null) + return DataHelper.escapeHTML(_t(name)); + return ins.getDestination().toBase32(); + } + private static final int WARN_LENGTH = 4; private static final int MAX_LENGTH = 4; private static final int MAX_ADVANCED_LENGTH = 7; diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/TunnelRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/TunnelRenderer.java index d97713e88..8c59725da 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/TunnelRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/TunnelRenderer.java @@ -3,6 +3,7 @@ package net.i2p.router.web.helpers; import java.io.IOException; import java.io.Serializable; import java.io.Writer; +import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -17,6 +18,8 @@ import net.i2p.data.TunnelId; import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.TunnelInfo; +import net.i2p.router.TunnelManagerFacade; +import net.i2p.router.TunnelPoolSettings; import net.i2p.router.tunnel.HopConfig; import net.i2p.router.tunnel.pool.TunnelPool; import net.i2p.router.web.HelperBase; @@ -37,8 +40,9 @@ class TunnelRenderer { } public void renderStatusHTML(Writer out) throws IOException { - TunnelPool ei = _context.tunnelManager().getInboundExploratoryPool(); - TunnelPool eo = _context.tunnelManager().getOutboundExploratoryPool(); + TunnelManagerFacade tm = _context.tunnelManager(); + TunnelPool ei = tm.getInboundExploratoryPool(); + TunnelPool eo = tm.getOutboundExploratoryPool(); out.write("

" + _t("Exploratory tunnels")); // links are set to float:right in CSS so they will be displayed in reverse order out.write(" [" + _t("configure") + "]"); @@ -46,37 +50,26 @@ class TunnelRenderer { out.write("

\n"); renderPool(out, ei, eo); - List destinations = null; - Map clientInboundPools = _context.tunnelManager().getInboundClientPools(); - Map clientOutboundPools = _context.tunnelManager().getOutboundClientPools(); - destinations = new ArrayList(clientInboundPools.keySet()); + Map clientInboundPools = tm.getInboundClientPools(); boolean debug = _context.getBooleanProperty(HelperBase.PROP_ADVANCED); - for (int i = 0; i < destinations.size(); i++) { - Hash client = destinations.get(i); + // display name to in pool + List sorted = new ArrayList(clientInboundPools.values()); + if (sorted.size() > 1) + DataHelper.sort(sorted, new TPComparator()); + for (TunnelPool in : sorted) { + Hash client = in.getSettings().getDestination(); boolean isLocal = _context.clientManager().isLocal(client); if ((!isLocal) && (!debug)) continue; - TunnelPool in = clientInboundPools.get(client); - TunnelPool outPool = clientOutboundPools.get(client); - if ((in != null && in.getSettings().getAliasOf() != null) || + TunnelPool outPool = tm.getOutboundPool(client); + if (in.getSettings().getAliasOf() != null || (outPool != null && outPool.getSettings().getAliasOf() != null)) { // skip aliases, we will print a header under the main tunnel pool below continue; } - // TODO the following code is duplicated in SummaryHelper - String name = (in != null) ? in.getSettings().getDestinationNickname() : null; - if ( (name == null) && (outPool != null) ) - name = outPool.getSettings().getDestinationNickname(); String b64 = client.toBase64().substring(0, 4); - String dname; - if (name == null) { - name = b64; - dname = client.toBase32(); - } else { - dname = DataHelper.escapeHTML(_t(name)); - } out.write("

" + _t("Client tunnels for {0}", dname)); + + "\" >" + _t("Client tunnels for {0}", getTunnelName(in))); if (isLocal) { // links are set to float:right in CSS so they will be displayed in reverse order out.write(" [" + _t("configure") + "]"); @@ -85,7 +78,7 @@ class TunnelRenderer { } else { out.write(" (" + _t("dead") + ")

\n"); } - if (in != null) { + // list aliases Set aliases = in.getSettings().getAliases(); if (aliases != null) { @@ -105,7 +98,7 @@ class TunnelRenderer { } } } - } + renderPool(out, in, outPool); } @@ -236,6 +229,37 @@ class TunnelRenderer { } } + /** + * Sort tunnels by the name of the tunnel + * @since 0.9.57 + */ + private class TPComparator implements Comparator { + private final Collator _comp = Collator.getInstance(); + public int compare(TunnelPool l, TunnelPool r) { + int rv = _comp.compare(getTunnelName(l), getTunnelName(r)); + if (rv != 0) + return rv; + return l.getSettings().getDestination().toBase32().compareTo(r.getSettings().getDestination().toBase32()); + } + } + + /** + * Get display name for the tunnel + * @since 0.9.57 + */ + private String getTunnelName(TunnelPool in) { + TunnelPoolSettings ins = in.getSettings(); + String name = ins.getDestinationNickname(); + if (name == null) { + TunnelPoolSettings outPool = _context.tunnelManager().getOutboundSettings(ins.getDestination()); + if (outPool != null) + name = outPool.getDestinationNickname(); + } + if (name != null) + return DataHelper.escapeHTML(_t(name)); + return ins.getDestination().toBase32(); + } + /** @since 0.9.35 */ private void writeGraphLinks(Writer out, TunnelPool in, TunnelPool outPool) throws IOException { if (in == null || outPool == null)