package net.i2p.router.web;
import java.io.IOException;
import java.text.Collator;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterAddress;
import net.i2p.data.RouterInfo;
import net.i2p.router.CommSystemFacade;
import net.i2p.router.Router;
import net.i2p.router.RouterVersion;
import net.i2p.router.TunnelPoolSettings;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.transport.ntcp.NTCPAddress;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
/**
* Simple helper to query the appropriate router for data necessary to render
* the summary sections on the router console.
*
* For the full summary bar use renderSummaryBar()
*/
public class SummaryHelper extends HelperBase {
// Opera 10.63 doesn't have the char, TODO check UA
//static final String THINSP = " / ";
static final String THINSP = " / ";
/**
* Retrieve the shortened 4 character ident for the router located within
* the current JVM at the given context.
*
*/
public String getIdent() {
if (_context == null) return "[no router]";
if (_context.routerHash() != null)
return _context.routerHash().toBase64().substring(0, 4);
else
return "[unknown]";
}
/**
* Retrieve the version number of the router.
*
*/
public String getVersion() {
return RouterVersion.FULL_VERSION;
}
/**
* Retrieve a pretty printed uptime count (ala 4d or 7h or 39m)
*
*/
public String getUptime() {
if (_context == null) return "[no router]";
Router router = _context.router();
if (router == null)
return "[not up]";
else
return DataHelper.formatDuration2(router.getUptime());
}
/**
this displayed offset, not skew - now handled in reachability()
private String timeSkew() {
if (_context == null) return "";
//if (!_context.clock().getUpdatedSuccessfully())
// return " (Unknown skew)";
long ms = _context.clock().getOffset();
long diff = Math.abs(ms);
if (diff < 3000)
return "";
return " (" + DataHelper.formatDuration2(diff) + " " + _("skew") + ")";
}
**/
public boolean allowReseed() {
return _context.netDb().isInitialized() &&
((_context.netDb().getKnownRouters() < 30) ||
Boolean.valueOf(_context.getProperty("i2p.alwaysAllowReseed")).booleanValue());
}
/** subtract one for ourselves, so if we know no other peers it displays zero */
public int getAllPeers() { return Math.max(_context.netDb().getKnownRouters() - 1, 0); }
public String getReachability() {
return reachability(); // + timeSkew();
// testing
//return reachability() +
// " Offset: " + DataHelper.formatDuration(_context.clock().getOffset()) +
// " Slew: " + DataHelper.formatDuration(((RouterClock)_context.clock()).getDeltaOffset());
}
private String reachability() {
if (_context.commSystem().isDummy())
return "VM Comm System";
if (_context.router().getUptime() > 60*1000 && (!_context.router().gracefulShutdownInProgress()) &&
!_context.clientManager().isAlive())
return _("ERR-Client Manager I2CP Error - check logs"); // not a router problem but the user should know
// Warn based on actual skew from peers, not update status, so if we successfully offset
// the clock, we don't complain.
//if (!_context.clock().getUpdatedSuccessfully())
long skew = _context.commSystem().getFramedAveragePeerClockSkew(33);
// Display the actual skew, not the offset
if (Math.abs(skew) > 30*1000)
return _("ERR-Clock Skew of {0}", DataHelper.formatDuration2(Math.abs(skew)));
if (_context.router().isHidden())
return _("Hidden");
RouterInfo routerInfo = _context.router().getRouterInfo();
if (routerInfo == null)
return _("Testing");
int status = _context.commSystem().getReachabilityStatus();
switch (status) {
case CommSystemFacade.STATUS_OK:
RouterAddress ra = routerInfo.getTargetAddress("NTCP");
if (ra == null || (new NTCPAddress(ra)).isPubliclyRoutable())
return _("OK");
return _("ERR-Private TCP Address");
case CommSystemFacade.STATUS_DIFFERENT:
return _("ERR-SymmetricNAT");
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
if (routerInfo.getTargetAddress("NTCP") != null)
return _("WARN-Firewalled with Inbound TCP Enabled");
if (((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled())
return _("WARN-Firewalled and Floodfill");
//if (_context.router().getRouterInfo().getCapabilities().indexOf('O') >= 0)
// return _("WARN-Firewalled and Fast");
return _("Firewalled");
case CommSystemFacade.STATUS_HOSED:
return _("ERR-UDP Port In Use - Set i2np.udp.internalPort=xxxx in advanced config and restart");
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
default:
ra = routerInfo.getTargetAddress("SSU");
if (ra == null && _context.router().getUptime() > 5*60*1000) {
if (getActivePeers() <= 0)
return _("ERR-No Active Peers, Check Network Connection and Firewall");
else if (_context.getProperty(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME) == null ||
_context.getProperty(ConfigNetHelper.PROP_I2NP_NTCP_PORT) == null)
return _("ERR-UDP Disabled and Inbound TCP host/port not set");
else
return _("WARN-Firewalled with UDP Disabled");
}
return _("Testing");
}
}
/**
* Retrieve amount of used memory.
*
*/
/********
public String getMemory() {
DecimalFormat integerFormatter = new DecimalFormat("###,###,##0");
long used = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024;
long usedPc = 100 - ((Runtime.getRuntime().freeMemory() * 100) / Runtime.getRuntime().totalMemory());
return integerFormatter.format(used) + "KB (" + usedPc + "%)";
}
********/
/**
* How many peers we are talking to now
*
*/
public int getActivePeers() {
if (_context == null)
return 0;
else
return _context.commSystem().countActivePeers();
}
/**
* Should we warn about a possible firewall problem?
*/
public boolean showFirewallWarning() {
return _context != null &&
_context.netDb().isInitialized() &&
_context.router().getUptime() > 2*60*1000 &&
(!_context.commSystem().isDummy()) &&
_context.commSystem().countActivePeers() <= 0 &&
_context.netDb().getKnownRouters() > 5;
}
/**
* How many active identities have we spoken with recently
*
*/
public int getActiveProfiles() {
if (_context == null)
return 0;
else
return _context.profileOrganizer().countActivePeers();
}
/**
* How many active peers the router ranks as fast.
*
*/
public int getFastPeers() {
if (_context == null)
return 0;
else
return _context.profileOrganizer().countFastPeers();
}
/**
* How many active peers the router ranks as having a high capacity.
*
*/
public int getHighCapacityPeers() {
if (_context == null)
return 0;
else
return _context.profileOrganizer().countHighCapacityPeers();
}
/**
* How many active peers the router ranks as well integrated.
*
*/
public int getWellIntegratedPeers() {
if (_context == null)
return 0;
//return _context.profileOrganizer().countWellIntegratedPeers();
return _context.peerManager().getPeersByCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL).size();
}
/**
* How many peers the router ranks as failing.
*
*/
/********
public int getFailingPeers() {
if (_context == null)
return 0;
else
return _context.profileOrganizer().countFailingPeers();
}
********/
/**
* How many peers totally suck.
*
*/
/********
public int getShitlistedPeers() {
if (_context == null)
return 0;
else
return _context.shitlist().getRouterCount();
}
********/
/**
* @return "x.xx / y.yy {K|M}"
*/
public String getSecondKBps() {
if (_context == null)
return "0 / 0";
return formatPair(_context.bandwidthLimiter().getReceiveBps(),
_context.bandwidthLimiter().getSendBps());
}
/**
* @return "x.xx / y.yy {K|M}"
*/
public String getFiveMinuteKBps() {
if (_context == null)
return "0 / 0";
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
double in = 0;
if (receiveRate != null) {
Rate r = receiveRate.getRate(5*60*1000);
if (r != null)
in = r.getAverageValue();
}
RateStat sendRate = _context.statManager().getRate("bw.sendRate");
double out = 0;
if (sendRate != null) {
Rate r = sendRate.getRate(5*60*1000);
if (r != null)
out = r.getAverageValue();
}
return formatPair(in, out);
}
/**
* @return "x.xx / y.yy {K|M}"
*/
public String getLifetimeKBps() {
if (_context == null)
return "0 / 0";
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
double in;
if (receiveRate == null)
in = 0;
else
in = receiveRate.getLifetimeAverageValue();
RateStat sendRate = _context.statManager().getRate("bw.sendRate");
double out;
if (sendRate == null)
out = 0;
else
out = sendRate.getLifetimeAverageValue();
return formatPair(in, out);
}
/**
* @return "x.xx / y.yy {K|M}"
*/
private static String formatPair(double in, double out) {
boolean mega = in >= 1024*1024 || out >= 1024*1024;
// scale both the same
if (mega) {
in /= 1024*1024;
out /= 1024*1024;
} else {
in /= 1024;
out /= 1024;
}
// control total width
DecimalFormat fmt;
if (in >= 1000 || out >= 1000)
fmt = new DecimalFormat("#0");
else if (in >= 100 || out >= 100)
fmt = new DecimalFormat("#0.0");
else
fmt = new DecimalFormat("#0.00");
return fmt.format(in) + THINSP + fmt.format(out) + " " +
(mega ? 'M' : 'K');
}
/**
* How much data have we received since the router started (pretty printed
* string with 2 decimal places and the appropriate units - GB/MB/KB/bytes)
*
*/
public String getInboundTransferred() {
if (_context == null)
return "0";
long received = _context.bandwidthLimiter().getTotalAllocatedInboundBytes();
return DataHelper.formatSize2(received) + 'B';
}
/**
* How much data have we sent since the router started (pretty printed
* string with 2 decimal places and the appropriate units - GB/MB/KB/bytes)
*
*/
public String getOutboundTransferred() {
if (_context == null)
return "0";
long sent = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes();
return DataHelper.formatSize2(sent) + 'B';
}
/**
* Client destinations connected locally.
*
* @return html section summary
*/
public String getDestinations() {
// convert the set to a list so we can sort by name and not lose duplicates
List").append(_("Local Destinations")).append("
");
for (Iterator
");
} else {
buf.append(" \n");
} else {
// green light
buf.append("");
else
buf.append("client.png\" alt=\"Client\" title=\"" + _("Client") + "\">");
buf.append("
");
if (name.length() < 18)
buf.append(name);
else
buf.append(name.substring(0,15)).append("…");
buf.append(" \n");
LeaseSet ls = _context.netDb().lookupLeaseSetLocally(h);
if (ls != null && _context.tunnelManager().getOutboundClientTunnelCount(h) > 0) {
long timeToExpire = ls.getEarliestLeaseDate() - _context.clock().now();
if (timeToExpire < 0) {
// red or yellow light
buf.append(" \n");
}
} else {
// yellow light
buf.append(" \n");
}
}
buf.append("").append(status).append("
\n");
}
if (updateAvailable() || unsignedUpdateAvailable()) {
if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) {
// nothing
} else if(
// isDone() is always false for now, see UpdateHandler
// ((!update.isDone()) &&
getAction() == null &&
getUpdateNonce() == null &&
ConfigRestartBean.getRestartTimeRemaining() > 12*60*1000) {
long nonce = _context.random().nextLong();
String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce");
if (prev != null)
System.setProperty("net.i2p.router.web.UpdateHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.UpdateHandler.nonce", nonce+"");
String uri = getRequestURI();
buf.append("\n");
}
}
return buf.toString();
}
/**
* The restart status and buttons
* @since 0.8.13 moved from SummaryBarRenderer
*/
public String getRestartStatus() {
return ConfigRestartBean.renderStatus(getRequestURI(), getAction(), getConsoleNonce());
}
/**
* The firewall status and reseed status/buttons
* @since 0.9 moved from SummaryBarRenderer
*/
public String getFirewallAndReseedStatus() {
StringBuilder buf = new StringBuilder(256);
if (showFirewallWarning()) {
buf.append("")
.append(_("Check network connection and NAT/firewall"))
.append("
");
}
boolean reseedInProgress = _context.netDb().reseedChecker().inProgress();
// If showing the reseed link is allowed
if (allowReseed()) {
if (reseedInProgress) {
// While reseed occurring, show status message instead
buf.append("").append(_context.netDb().reseedChecker().getStatus()).append("
");
} else {
// While no reseed occurring, show reseed link
long nonce = _context.random().nextLong();
String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
String uri = getRequestURI();
buf.append("