forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head 60a9a2297abeaf042645e3f0bc8d106f1ff585bf)
to branch 'i2p.i2p.zzz.test2' (head 6ff6f0bcee835d32aad62449a37f5171afde915a)
This commit is contained in:
@ -100,15 +100,15 @@
|
||||
<target name="war" depends="jar, bundle, warUpToDate, listChangedFiles" unless="war.uptodate" >
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<copy todir="build/icons/.icons" >
|
||||
<fileset dir="../icons/" />
|
||||
<copy todir="build/resources/.resources" >
|
||||
<fileset dir="../resources/" />
|
||||
</copy>
|
||||
<!-- mime.properties must be in with the classes -->
|
||||
<copy file="../mime.properties" todir="build/obj/org/klomp/snark/web" />
|
||||
<war destfile="../i2psnark.war" webxml="../web.xml" >
|
||||
<!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war -->
|
||||
<classes dir="./build/obj" includes="**/web/*" />
|
||||
<fileset dir="build/icons/" />
|
||||
<fileset dir="build/resources/" />
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
@ -121,7 +121,7 @@
|
||||
|
||||
<target name="warUpToDate">
|
||||
<uptodate property="war.uptodate" targetfile="../i2psnark.war" >
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../icons/* ../web.xml" />
|
||||
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../resources/**/* ../web.xml" />
|
||||
</uptodate>
|
||||
</target>
|
||||
|
||||
|
@ -57,8 +57,8 @@ public class Peer implements Comparable<Peer>
|
||||
private DataOutputStream dout;
|
||||
|
||||
/** running counters */
|
||||
private long downloaded;
|
||||
private long uploaded;
|
||||
private final AtomicLong downloaded = new AtomicLong();
|
||||
private final AtomicLong uploaded = new AtomicLong();
|
||||
|
||||
// Keeps state for in/out connections. Non-null when the handshake
|
||||
// was successful, the connection setup and runs
|
||||
@ -618,7 +618,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void downloaded(int size) {
|
||||
downloaded += size;
|
||||
downloaded.addAndGet(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -626,7 +626,7 @@ public class Peer implements Comparable<Peer>
|
||||
* @since 0.8.4
|
||||
*/
|
||||
public void uploaded(int size) {
|
||||
uploaded += size;
|
||||
uploaded.addAndGet(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -635,7 +635,7 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public long getDownloaded()
|
||||
{
|
||||
return downloaded;
|
||||
return downloaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -644,7 +644,7 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public long getUploaded()
|
||||
{
|
||||
return uploaded;
|
||||
return uploaded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -652,8 +652,8 @@ public class Peer implements Comparable<Peer>
|
||||
*/
|
||||
public void resetCounters()
|
||||
{
|
||||
downloaded = 0;
|
||||
uploaded = 0;
|
||||
downloaded.set(0);
|
||||
uploaded.set(0);
|
||||
}
|
||||
|
||||
public long getInactiveTime() {
|
||||
|
@ -27,7 +27,6 @@ import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -245,16 +244,19 @@ public class Snark
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
/****
|
||||
Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
|
||||
StorageListener slistener, CoordinatorListener clistener) {
|
||||
this(util, torrent, ip, user_port, slistener, clistener, null, null, null, true, ".");
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* single torrent - via router
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
/****
|
||||
public Snark(I2PAppContext ctx, Properties opts, String torrent,
|
||||
StorageListener slistener, boolean start, String rootDir) {
|
||||
this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir);
|
||||
@ -284,6 +286,7 @@ public class Snark
|
||||
if (start)
|
||||
this.startTorrent();
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* multitorrent
|
||||
@ -515,18 +518,13 @@ public class Snark
|
||||
|
||||
// Create a new ID and fill it with something random. First nine
|
||||
// zeros bytes, then three bytes filled with snark and then
|
||||
// sixteen random bytes.
|
||||
// eight random bytes.
|
||||
byte snark = (((3 + 7 + 10) * (1000 - 8)) / 992) - 17;
|
||||
byte[] rv = new byte[20];
|
||||
Random random = I2PAppContext.getGlobalContext().random();
|
||||
int i;
|
||||
for (i = 0; i < 9; i++)
|
||||
rv[i] = 0;
|
||||
rv[i++] = snark;
|
||||
rv[i++] = snark;
|
||||
rv[i++] = snark;
|
||||
while (i < 20)
|
||||
rv[i++] = (byte)random.nextInt(256);
|
||||
rv[9] = snark;
|
||||
rv[10] = snark;
|
||||
rv[11] = snark;
|
||||
I2PAppContext.getGlobalContext().random().nextBytes(rv, 12, 8);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -958,6 +956,7 @@ public class Snark
|
||||
* non-valid argument list. The given listeners will be
|
||||
* passed to all components that take one.
|
||||
*/
|
||||
/****
|
||||
private static Snark parseArguments(String[] args,
|
||||
StorageListener slistener,
|
||||
CoordinatorListener clistener)
|
||||
@ -972,6 +971,7 @@ public class Snark
|
||||
int i = 0;
|
||||
while (i < args.length)
|
||||
{
|
||||
****/
|
||||
/*
|
||||
if (args[i].equals("--debug"))
|
||||
{
|
||||
@ -993,7 +993,9 @@ public class Snark
|
||||
catch (NumberFormatException nfe) { }
|
||||
}
|
||||
}
|
||||
else */ if (args[i].equals("--port"))
|
||||
else */
|
||||
/****
|
||||
if (args[i].equals("--port"))
|
||||
{
|
||||
if (args.length - 1 < i + 1)
|
||||
usage("--port needs port number to listen on");
|
||||
@ -1099,6 +1101,7 @@ public class Snark
|
||||
System.out.println
|
||||
(" \tor (with --share) a file to share.");
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Aborts program abnormally.
|
||||
|
@ -61,7 +61,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
|
||||
private static final String DEFAULT_NAME = "i2psnark";
|
||||
public static final String PROP_CONFIG_FILE = "i2psnark.configFile";
|
||||
private static final String WARBASE = "/.icons/";
|
||||
private static final String WARBASE = "/.resources/";
|
||||
private static final char HELLIP = '\u2026';
|
||||
|
||||
public I2PSnarkServlet() {
|
||||
@ -194,22 +194,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
resp.setHeader("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'");
|
||||
resp.setHeader("X-XSS-Protection", "1; mode=block");
|
||||
|
||||
String peerParam = req.getParameter("p");
|
||||
String stParam = req.getParameter("st");
|
||||
String peerString;
|
||||
if (peerParam == null || (!_manager.util().connected()) ||
|
||||
peerParam.replaceAll("[a-zA-Z0-9~=-]", "").length() > 0) { // XSS
|
||||
peerString = "";
|
||||
} else {
|
||||
peerString = "?p=" + DataHelper.stripHTML(peerParam);
|
||||
}
|
||||
if (stParam != null && !stParam.equals("0")) {
|
||||
stParam = DataHelper.stripHTML(stParam);
|
||||
if (peerString.length() > 0)
|
||||
peerString += "&st=" + stParam;
|
||||
else
|
||||
peerString = "?st="+ stParam;
|
||||
}
|
||||
String pOverride = _manager.util().connected() ? null : "";
|
||||
String peerString = getQueryString(req, pOverride, null, null);
|
||||
|
||||
// AJAX for mainsection
|
||||
if ("/.ajax/xhr1.html".equals(path)) {
|
||||
@ -292,6 +278,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write(_("Configuration"));
|
||||
else
|
||||
out.write(_("Anonymous BitTorrent Client"));
|
||||
String peerParam = req.getParameter("p");
|
||||
if ("2".equals(peerParam))
|
||||
out.write(" | Debug Mode");
|
||||
out.write("</title>\n");
|
||||
@ -413,13 +400,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
boolean isForm = _manager.util().connected() || !snarks.isEmpty();
|
||||
if (isForm) {
|
||||
out.write("<form action=\"_post\" method=\"POST\">\n");
|
||||
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n");
|
||||
// don't lose peer setting
|
||||
if (peerParam != null)
|
||||
out.write("<input type=\"hidden\" name=\"p\" value=\"" + peerParam + "\" >\n");
|
||||
// ...or st setting
|
||||
if (stParam != null)
|
||||
out.write("<input type=\"hidden\" name=\"st\" value=\"" + stParam + "\" >\n");
|
||||
writeHiddenInputs(out, req, null);
|
||||
}
|
||||
out.write(TABLE_HEADER);
|
||||
|
||||
@ -441,18 +422,29 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
int pageSize = Math.max(_manager.getPageSize(), 5);
|
||||
|
||||
out.write("<tr><th><img border=\"0\" src=\"" + _imgPath + "status.png\" title=\"");
|
||||
out.write(_("Status"));
|
||||
String currentSort = req.getParameter("sort");
|
||||
boolean showSort = total > 1;
|
||||
out.write("<tr><th>");
|
||||
String sort = ("2".equals(currentSort)) ? "-2" : "2";
|
||||
if (showSort) {
|
||||
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
|
||||
out.write("\">");
|
||||
}
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "status.png\" title=\"");
|
||||
if (showSort)
|
||||
out.write(_("Sort by {0}", _("Status")));
|
||||
else
|
||||
out.write(_("Status"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Status"));
|
||||
out.write("\"></th>\n<th>");
|
||||
out.write("\">");
|
||||
if (showSort)
|
||||
out.write("</a>");
|
||||
out.write("</th>\n<th>");
|
||||
if (_manager.util().connected() && !snarks.isEmpty()) {
|
||||
out.write(" <a href=\"" + _contextPath + '/');
|
||||
if (peerParam != null) {
|
||||
if (stParam != null) {
|
||||
out.write("?st=");
|
||||
out.write(stParam);
|
||||
}
|
||||
// disable peer view
|
||||
out.write("\">");
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "hidepeers.png\" title=\"");
|
||||
out.write(_("Hide Peers"));
|
||||
@ -460,11 +452,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write(_("Hide Peers"));
|
||||
out.write("\">");
|
||||
} else {
|
||||
out.write("?p=1");
|
||||
if (stParam != null) {
|
||||
out.write("&st=");
|
||||
out.write(stParam);
|
||||
}
|
||||
// enable peer view
|
||||
out.write(getQueryString(req, "1", null, null));
|
||||
out.write("\">");
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "showpeers.png\" title=\"");
|
||||
out.write(_("Show Peers"));
|
||||
@ -475,56 +464,158 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write("</a><br>\n");
|
||||
}
|
||||
out.write("</th>\n<th colspan=\"2\" align=\"left\">");
|
||||
// cycle through sort by name or type
|
||||
boolean isTypeSort = false;
|
||||
if (showSort) {
|
||||
if (currentSort == null || "0".equals(currentSort) || "1".equals(currentSort)) {
|
||||
sort = "-1";
|
||||
} else if ("-1".equals(currentSort)) {
|
||||
sort = "12";
|
||||
isTypeSort = true;
|
||||
} else if ("12".equals(currentSort)) {
|
||||
sort = "-12";
|
||||
isTypeSort = true;
|
||||
} else {
|
||||
sort = "";
|
||||
}
|
||||
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
|
||||
out.write("\">");
|
||||
}
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "torrent.png\" title=\"");
|
||||
out.write(_("Torrent"));
|
||||
if (showSort)
|
||||
out.write(_("Sort by {0}", (isTypeSort ? _("File type") : _("Torrent"))));
|
||||
else
|
||||
out.write(_("Torrent"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Torrent"));
|
||||
out.write("\"></th>\n<th align=\"center\">");
|
||||
out.write("\">");
|
||||
if (showSort)
|
||||
out.write("</a>");
|
||||
out.write("</th>\n<th align=\"center\">");
|
||||
if (total > 0 && (start > 0 || total > pageSize)) {
|
||||
writePageNav(out, start, pageSize, total, peerParam, noThinsp);
|
||||
writePageNav(out, req, start, pageSize, total, noThinsp);
|
||||
}
|
||||
out.write("</th>\n<th align=\"right\">");
|
||||
if (_manager.util().connected() && !snarks.isEmpty()) {
|
||||
if (showSort) {
|
||||
sort = ("4".equals(currentSort)) ? "-4" : "4";
|
||||
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
|
||||
out.write("\">");
|
||||
}
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "eta.png\" title=\"");
|
||||
out.write(_("Estimated time remaining"));
|
||||
if (showSort)
|
||||
out.write(_("Sort by {0}", _("Estimated time remaining")));
|
||||
else
|
||||
out.write(_("Estimated time remaining"));
|
||||
out.write("\" alt=\"");
|
||||
// Translators: Please keep short or translate as " "
|
||||
out.write(_("ETA"));
|
||||
out.write("\">");
|
||||
if (showSort)
|
||||
out.write("</a>");
|
||||
}
|
||||
out.write("</th>\n<th align=\"right\">");
|
||||
// cycle through sort by size or downloaded
|
||||
boolean isDlSort = false;
|
||||
if (showSort) {
|
||||
if ("5".equals(currentSort)) {
|
||||
sort = "-5";
|
||||
} else if ("-5".equals(currentSort)) {
|
||||
sort = "6";
|
||||
isDlSort = true;
|
||||
} else if ("6".equals(currentSort)) {
|
||||
sort = "-6";
|
||||
isDlSort = true;
|
||||
} else {
|
||||
sort = "5";
|
||||
}
|
||||
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
|
||||
out.write("\">");
|
||||
}
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "head_rx.png\" title=\"");
|
||||
out.write(_("Downloaded"));
|
||||
if (showSort)
|
||||
out.write(_("Sort by {0}", (isDlSort ? _("Downloaded") : _("Size"))));
|
||||
else
|
||||
out.write(_("Downloaded"));
|
||||
out.write("\" alt=\"");
|
||||
// Translators: Please keep short or translate as " "
|
||||
out.write(_("RX"));
|
||||
out.write("\">");
|
||||
if (showSort)
|
||||
out.write("</a>");
|
||||
out.write("</th>\n<th align=\"right\">");
|
||||
boolean isRatSort = false;
|
||||
if (!snarks.isEmpty()) {
|
||||
// cycle through sort by uploaded or ratio
|
||||
boolean nextRatSort = false;
|
||||
if (showSort) {
|
||||
if ("7".equals(currentSort)) {
|
||||
sort = "-7";
|
||||
} else if ("-7".equals(currentSort)) {
|
||||
sort = "11";
|
||||
nextRatSort = true;
|
||||
} else if ("11".equals(currentSort)) {
|
||||
sort = "-11";
|
||||
nextRatSort = true;
|
||||
isRatSort = true;
|
||||
} else if ("-11".equals(currentSort)) {
|
||||
sort = "7";
|
||||
isRatSort = true;
|
||||
} else {
|
||||
sort = "7";
|
||||
}
|
||||
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
|
||||
out.write("\">");
|
||||
}
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "head_tx.png\" title=\"");
|
||||
out.write(_("Uploaded"));
|
||||
if (showSort)
|
||||
out.write(_("Sort by {0}", (nextRatSort ? _("Upload ratio") : _("Uploaded"))));
|
||||
else
|
||||
out.write(_("Uploaded"));
|
||||
out.write("\" alt=\"");
|
||||
// Translators: Please keep short or translate as " "
|
||||
out.write(_("TX"));
|
||||
out.write("\">");
|
||||
if (showSort)
|
||||
out.write("</a>");
|
||||
}
|
||||
out.write("</th>\n<th align=\"right\">");
|
||||
if (_manager.util().connected() && !snarks.isEmpty()) {
|
||||
if (showSort) {
|
||||
sort = ("8".equals(currentSort)) ? "-8" : "8";
|
||||
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
|
||||
out.write("\">");
|
||||
}
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "head_rxspeed.png\" title=\"");
|
||||
out.write(_("Down Rate"));
|
||||
if (showSort)
|
||||
out.write(_("Sort by {0}", _("Down Rate")));
|
||||
else
|
||||
out.write(_("Down Rate"));
|
||||
out.write("\" alt=\"");
|
||||
// Translators: Please keep short or translate as " "
|
||||
out.write(_("RX Rate"));
|
||||
out.write(" \">");
|
||||
out.write("\">");
|
||||
if (showSort)
|
||||
out.write("</a>");
|
||||
}
|
||||
out.write("</th>\n<th align=\"right\">");
|
||||
if (_manager.util().connected() && !snarks.isEmpty()) {
|
||||
if (showSort) {
|
||||
sort = ("9".equals(currentSort)) ? "-9" : "9";
|
||||
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
|
||||
out.write("\">");
|
||||
}
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "head_txspeed.png\" title=\"");
|
||||
out.write(_("Up Rate"));
|
||||
if (showSort)
|
||||
out.write(_("Sort by {0}", _("Up Rate")));
|
||||
else
|
||||
out.write(_("Up Rate"));
|
||||
out.write("\" alt=\"");
|
||||
// Translators: Please keep short or translate as " "
|
||||
out.write(_("TX Rate"));
|
||||
out.write(" \">");
|
||||
out.write("\">");
|
||||
if (showSort)
|
||||
out.write("</a>");
|
||||
}
|
||||
out.write("</th>\n<th align=\"center\">");
|
||||
|
||||
@ -580,12 +671,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
String uri = _contextPath + '/';
|
||||
boolean showDebug = "2".equals(peerParam);
|
||||
|
||||
String stParamStr = stParam == null ? "" : "&st=" + stParam;
|
||||
for (int i = 0; i < total; i++) {
|
||||
Snark snark = snarks.get(i);
|
||||
boolean showPeers = showDebug || "1".equals(peerParam) || Base64.encode(snark.getInfoHash()).equals(peerParam);
|
||||
boolean hide = i < start || i >= start + pageSize;
|
||||
displaySnark(out, snark, uri, i, stats, showPeers, isDegraded, noThinsp, showDebug, hide, stParamStr);
|
||||
displaySnark(out, req, snark, uri, i, stats, showPeers, isDegraded, noThinsp, showDebug, hide, isRatSort);
|
||||
}
|
||||
|
||||
if (total == 0) {
|
||||
@ -636,17 +726,105 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
return start == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidden inputs for nonce and paramters p, st, and sort
|
||||
*
|
||||
* @param out writes to it
|
||||
* @param action if non-null, add it as the action
|
||||
* @since 0.9.16
|
||||
*/
|
||||
private void writeHiddenInputs(PrintWriter out, HttpServletRequest req, String action) {
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
writeHiddenInputs(buf, req, action);
|
||||
out.write(buf.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* hidden inputs for nonce and paramters p, st, and sort
|
||||
*
|
||||
* @param out appends to it
|
||||
* @param action if non-null, add it as the action
|
||||
* @since 0.9.16
|
||||
*/
|
||||
private void writeHiddenInputs(StringBuilder buf, HttpServletRequest req, String action) {
|
||||
buf.append("<input type=\"hidden\" name=\"nonce\" value=\"")
|
||||
.append(_nonce).append("\" >\n");
|
||||
String peerParam = req.getParameter("p");
|
||||
if (peerParam != null) {
|
||||
buf.append("<input type=\"hidden\" name=\"p\" value=\"")
|
||||
.append(DataHelper.stripHTML(peerParam)).append("\" >\n");
|
||||
}
|
||||
String stParam = req.getParameter("st");
|
||||
if (stParam != null) {
|
||||
buf.append("<input type=\"hidden\" name=\"st\" value=\"")
|
||||
.append(DataHelper.stripHTML(stParam)).append("\" >\n");
|
||||
}
|
||||
String soParam = req.getParameter("sort");
|
||||
if (soParam != null) {
|
||||
buf.append("<input type=\"hidden\" name=\"sort\" value=\"")
|
||||
.append(DataHelper.stripHTML(soParam)).append("\" >\n");
|
||||
}
|
||||
if (action != null) {
|
||||
buf.append("<input type=\"hidden\" name=\"action\" value=\"")
|
||||
.append(action).append("\" >\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build HTML-escaped and stripped query string
|
||||
*
|
||||
* @param p override or "" for default or null to keep the same as in req
|
||||
* @param st override or "" for default or null to keep the same as in req
|
||||
* @param so override or "" for default or null to keep the same as in req
|
||||
* @return non-null, possibly empty
|
||||
* @since 0.9.16
|
||||
*/
|
||||
private static String getQueryString(HttpServletRequest req, String p, String st, String so) {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
if (p == null) {
|
||||
p = req.getParameter("p");
|
||||
if (p != null)
|
||||
p = DataHelper.stripHTML(p);
|
||||
}
|
||||
if (p != null && !p.equals(""))
|
||||
buf.append("?p=").append(p);
|
||||
if (so == null) {
|
||||
so = req.getParameter("sort");
|
||||
if (so != null)
|
||||
so = DataHelper.stripHTML(so);
|
||||
}
|
||||
if (so != null && !so.equals("")) {
|
||||
if (buf.length() <= 0)
|
||||
buf.append("?sort=");
|
||||
else
|
||||
buf.append("&sort=");
|
||||
buf.append(so);
|
||||
}
|
||||
if (st == null) {
|
||||
st = req.getParameter("st");
|
||||
if (st != null)
|
||||
st = DataHelper.stripHTML(st);
|
||||
}
|
||||
if (st != null && !st.equals("")) {
|
||||
if (buf.length() <= 0)
|
||||
buf.append("?st=");
|
||||
else
|
||||
buf.append("&st=");
|
||||
buf.append(st);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
private void writePageNav(PrintWriter out, int start, int pageSize, int total,
|
||||
String peerParam, boolean noThinsp) {
|
||||
private void writePageNav(PrintWriter out, HttpServletRequest req, int start, int pageSize, int total,
|
||||
boolean noThinsp) {
|
||||
// Page nav
|
||||
if (start > 0) {
|
||||
// First
|
||||
out.write("<a href=\"" + _contextPath);
|
||||
if (peerParam != null)
|
||||
out.write("?p=" + peerParam);
|
||||
out.write(getQueryString(req, null, "", null));
|
||||
out.write("\">" +
|
||||
"<img alt=\"" + _("First") + "\" title=\"" + _("First page") + "\" border=\"0\" src=\"" +
|
||||
_imgPath + "control_rewind_blue.png\">" +
|
||||
@ -655,9 +833,9 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//if (prev > 0) {
|
||||
if (true) {
|
||||
// Back
|
||||
out.write(" <a href=\"" + _contextPath + "?st=" + prev);
|
||||
if (peerParam != null)
|
||||
out.write("&p=" + peerParam);
|
||||
out.write(" <a href=\"" + _contextPath);
|
||||
String sprev = (prev > 0) ? Integer.toString(prev) : "";
|
||||
out.write(getQueryString(req, null, sprev, null));
|
||||
out.write("\">" +
|
||||
"<img alt=\"" + _("Prev") + "\" title=\"" + _("Previous page") + "\" border=\"0\" src=\"" +
|
||||
_imgPath + "control_back_blue.png\">" +
|
||||
@ -690,9 +868,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//if (next + pageSize < total) {
|
||||
if (true) {
|
||||
// Next
|
||||
out.write(" <a href=\"" + _contextPath + "?st=" + next);
|
||||
if (peerParam != null)
|
||||
out.write("&p=" + peerParam);
|
||||
out.write(" <a href=\"" + _contextPath);
|
||||
out.write(getQueryString(req, null, Integer.toString(next), null));
|
||||
out.write("\">" +
|
||||
"<img alt=\"" + _("Next") + "\" title=\"" + _("Next page") + "\" border=\"0\" src=\"" +
|
||||
_imgPath + "control_play_blue.png\">" +
|
||||
@ -700,9 +877,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
// Last
|
||||
int last = ((total - 1) / pageSize) * pageSize;
|
||||
out.write(" <a href=\"" + _contextPath + "?st=" + last);
|
||||
if (peerParam != null)
|
||||
out.write("&p=" + peerParam);
|
||||
out.write(" <a href=\"" + _contextPath);
|
||||
out.write(getQueryString(req, null, Integer.toString(last), null));
|
||||
out.write("\">" +
|
||||
"<img alt=\"" + _("Last") + "\" title=\"" + _("Last page") + "\" border=\"0\" src=\"" +
|
||||
_imgPath + "control_fastforward_blue.png\">" +
|
||||
@ -1190,34 +1366,22 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort alphabetically in current locale, ignore case, ignore leading "the "
|
||||
* (I guess this is worth it, a lot of torrents start with "The "
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private static class TorrentNameComparator implements Comparator<Snark>, Serializable {
|
||||
|
||||
public int compare(Snark l, Snark r) {
|
||||
// put downloads and magnets first
|
||||
if (l.getStorage() == null && r.getStorage() != null)
|
||||
return -1;
|
||||
if (l.getStorage() != null && r.getStorage() == null)
|
||||
return 1;
|
||||
String ls = l.getBaseName();
|
||||
String llc = ls.toLowerCase(Locale.US);
|
||||
if (llc.startsWith("the ") || llc.startsWith("the.") || llc.startsWith("the_"))
|
||||
ls = ls.substring(4);
|
||||
String rs = r.getBaseName();
|
||||
String rlc = rs.toLowerCase(Locale.US);
|
||||
if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_"))
|
||||
rs = rs.substring(4);
|
||||
return Collator.getInstance().compare(ls, rs);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Snark> getSortedSnarks(HttpServletRequest req) {
|
||||
ArrayList<Snark> rv = new ArrayList<Snark>(_manager.getTorrents());
|
||||
Collections.sort(rv, new TorrentNameComparator());
|
||||
if (rv.size() > 1) {
|
||||
int sort = 0;
|
||||
String ssort = req.getParameter("sort");
|
||||
if (ssort != null) {
|
||||
try {
|
||||
sort = Integer.parseInt(ssort);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
try {
|
||||
Collections.sort(rv, Sorters.getComparator(sort, this));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// Java 7 TimSort - may be unstable
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -1229,11 +1393,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
*
|
||||
* @param stats in/out param (totals)
|
||||
* @param statsOnly if true, output nothing, update stats only
|
||||
* @param stParam non null; empty or e.g. &st=10
|
||||
*/
|
||||
private void displaySnark(PrintWriter out, Snark snark, String uri, int row, long stats[], boolean showPeers,
|
||||
private void displaySnark(PrintWriter out, HttpServletRequest req,
|
||||
Snark snark, String uri, int row, long stats[], boolean showPeers,
|
||||
boolean isDegraded, boolean noThinsp, boolean showDebug, boolean statsOnly,
|
||||
String stParam) throws IOException {
|
||||
boolean showRatios) throws IOException {
|
||||
// stats
|
||||
long uploaded = snark.getUploaded();
|
||||
stats[0] += snark.getDownloaded();
|
||||
@ -1335,7 +1499,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (curPeers > 0 && !showPeers)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + img + ".png\" title=\"" + txt + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus\">" + txt +
|
||||
": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + stParam + "\">" +
|
||||
": <a href=\"" + uri + getQueryString(req, Base64.encode(snark.getInfoHash()), null, null) + "\">" +
|
||||
curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||
else
|
||||
@ -1351,7 +1515,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "downloading.png\" title=\"" + _("OK") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus\">" + _("OK") +
|
||||
": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + stParam + "\">" +
|
||||
": <a href=\"" + uri + getQueryString(req, Base64.encode(snark.getInfoHash()), null, null) + "\">" +
|
||||
curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||
else if (isRunning && curPeers > 0 && downBps > 0)
|
||||
@ -1362,7 +1526,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
else if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Stalled") + "\"></td>" +
|
||||
"<td class=\"snarkTorrentStatus\">" + _("Stalled") +
|
||||
": <a href=\"" + uri + "?p=" + Base64.encode(snark.getInfoHash()) + stParam + "\">" +
|
||||
": <a href=\"" + uri + getQueryString(req, Base64.encode(snark.getInfoHash()), null, null) + "\">" +
|
||||
curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||
else if (isRunning && curPeers > 0)
|
||||
@ -1466,8 +1630,17 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// out.write("??"); // no meta size yet
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentUploaded\">");
|
||||
if (isValid && uploaded > 0)
|
||||
out.write(formatSize(uploaded));
|
||||
if (isValid) {
|
||||
if (showRatios) {
|
||||
if (total > 0) {
|
||||
double ratio = uploaded / ((double) total);
|
||||
out.write((new DecimalFormat("0.000")).format(ratio));
|
||||
out.write(" x");
|
||||
}
|
||||
} else if (uploaded > 0) {
|
||||
out.write(formatSize(uploaded));
|
||||
}
|
||||
}
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td align=\"right\" class=\"snarkTorrentRateDown\">");
|
||||
if (isRunning && needed > 0)
|
||||
@ -1484,7 +1657,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
} else if (isRunning) {
|
||||
// Stop Button
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Stop_" + b64 + "&nonce=" + _nonce + stParam + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Stop_" + b64 + "&nonce=" + _nonce +
|
||||
getQueryString(req, "", null, null).replace("?", "&") + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Stop_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Stop the torrent"));
|
||||
@ -1498,7 +1672,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// Start Button
|
||||
// This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Start_" + b64 + "&nonce=" + _nonce + stParam + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Start_" + b64 + "&nonce=" + _nonce +
|
||||
getQueryString(req, "", null, null).replace("?", "&") + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Start_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Start the torrent"));
|
||||
@ -1512,7 +1687,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// Remove Button
|
||||
// Doesnt work with Opera so use noThinsp instead of isDegraded
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Remove_" + b64 + "&nonce=" + _nonce + stParam + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Remove_" + b64 + "&nonce=" + _nonce +
|
||||
getQueryString(req, "", null, null).replace("?", "&") + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Remove_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Remove the torrent from the active list, deleting the .torrent file"));
|
||||
@ -1533,7 +1709,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// Delete Button
|
||||
// Doesnt work with Opera so use noThinsp instead of isDegraded
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Delete_" + b64 + "&nonce=" + _nonce + stParam + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Delete_" + b64 + "&nonce=" + _nonce +
|
||||
getQueryString(req, "", null, null).replace("?", "&") + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Delete_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Delete the .torrent file and the associated data file(s)"));
|
||||
@ -1842,12 +2019,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write("<div class=\"snarkNewTorrent\">\n");
|
||||
// *not* enctype="multipart/form-data", so that the input type=file sends the filename, not the file
|
||||
out.write("<form action=\"_post\" method=\"POST\">\n");
|
||||
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n");
|
||||
out.write("<input type=\"hidden\" name=\"action\" value=\"Add\" >\n");
|
||||
// don't lose peer setting
|
||||
String peerParam = req.getParameter("p");
|
||||
if (peerParam != null)
|
||||
out.write("<input type=\"hidden\" name=\"p\" value=\"" + DataHelper.stripHTML(peerParam) + "\" >\n");
|
||||
writeHiddenInputs(out, req, "Add");
|
||||
out.write("<div class=\"addtorrentsection\"><span class=\"snarkConfigTitle\">");
|
||||
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "add.png\"> ");
|
||||
out.write(_("Add Torrent"));
|
||||
@ -1874,12 +2046,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write("<a name=\"add\"></a><div class=\"newtorrentsection\"><div class=\"snarkNewTorrent\">\n");
|
||||
// *not* enctype="multipart/form-data", so that the input type=file sends the filename, not the file
|
||||
out.write("<form action=\"_post\" method=\"POST\">\n");
|
||||
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n");
|
||||
out.write("<input type=\"hidden\" name=\"action\" value=\"Create\" >\n");
|
||||
// don't lose peer setting
|
||||
String peerParam = req.getParameter("p");
|
||||
if (peerParam != null)
|
||||
out.write("<input type=\"hidden\" name=\"p\" value=\"" + DataHelper.stripHTML(peerParam) + "\" >\n");
|
||||
writeHiddenInputs(out, req, "Create");
|
||||
out.write("<span class=\"snarkConfigTitle\">");
|
||||
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "create.png\"> ");
|
||||
out.write(_("Create Torrent"));
|
||||
@ -1945,10 +2112,9 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//int seedPct = 0;
|
||||
|
||||
out.write("<form action=\"" + _contextPath + "/configure\" method=\"POST\">\n" +
|
||||
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n" +
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n" +
|
||||
"<input type=\"hidden\" name=\"action\" value=\"Save\" >\n" +
|
||||
"<span class=\"snarkConfigTitle\">" +
|
||||
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n");
|
||||
writeHiddenInputs(out, req, "Save");
|
||||
out.write("<span class=\"snarkConfigTitle\">" +
|
||||
"<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
|
||||
out.write(_("Configuration"));
|
||||
out.write("</span><hr>\n" +
|
||||
@ -2131,10 +2297,9 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
private void writeTrackerForm(PrintWriter out, HttpServletRequest req) throws IOException {
|
||||
StringBuilder buf = new StringBuilder(1024);
|
||||
buf.append("<form action=\"" + _contextPath + "/configure\" method=\"POST\">\n" +
|
||||
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n" +
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n" +
|
||||
"<input type=\"hidden\" name=\"action\" value=\"Save2\" >\n" +
|
||||
"<span class=\"snarkConfigTitle\">" +
|
||||
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n");
|
||||
writeHiddenInputs(buf, req, "Save2");
|
||||
buf.append("<span class=\"snarkConfigTitle\">" +
|
||||
"<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
|
||||
buf.append(_("Trackers"));
|
||||
buf.append("</span><hr>\n" +
|
||||
@ -2435,6 +2600,10 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// dummy
|
||||
r = new File("");
|
||||
}
|
||||
|
||||
boolean showPriority = snark != null && snark.getStorage() != null && !snark.getStorage().complete() &&
|
||||
r.isDirectory();
|
||||
|
||||
StringBuilder buf=new StringBuilder(4096);
|
||||
buf.append(DOCTYPE).append("<HTML><HEAD><TITLE>");
|
||||
if (title.endsWith("/"))
|
||||
@ -2442,8 +2611,14 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
String directory = title;
|
||||
title = _("Torrent") + ": " + DataHelper.escapeHTML(title);
|
||||
buf.append(title);
|
||||
buf.append("</TITLE>").append(HEADER_A).append(_themePath).append(HEADER_B).append("<link rel=\"shortcut icon\" href=\"" + _themePath + "favicon.ico\">" +
|
||||
"</HEAD><BODY>\n<center><div class=\"snarknavbar\"><a href=\"").append(_contextPath).append("/\" title=\"Torrents\"");
|
||||
buf.append("</TITLE>\n").append(HEADER_A).append(_themePath).append(HEADER_B)
|
||||
.append("<link rel=\"shortcut icon\" href=\"" + _themePath + "favicon.ico\">\n");
|
||||
if (showPriority)
|
||||
buf.append("<script src=\"").append(_contextPath).append(WARBASE + "js/folder.js\" type=\"text/javascript\"></script>\n");
|
||||
buf.append("</HEAD><BODY");
|
||||
if (showPriority)
|
||||
buf.append(" onload=\"setupbuttons()\"");
|
||||
buf.append(">\n<center><div class=\"snarknavbar\"><a href=\"").append(_contextPath).append("/\" title=\"Torrents\"");
|
||||
buf.append(" class=\"snarkRefresh\"><img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("arrow_refresh.png\"> ");
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
buf.append(_("I2PSnark"));
|
||||
@ -2453,8 +2628,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
|
||||
if (parent) // always true
|
||||
buf.append("<div class=\"page\"><div class=\"mainsection\">");
|
||||
boolean showPriority = snark != null && snark.getStorage() != null && !snark.getStorage().complete() &&
|
||||
r.isDirectory();
|
||||
if (showPriority) {
|
||||
buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n");
|
||||
buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\" >\n");
|
||||
@ -2594,9 +2767,20 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append(":</b> ")
|
||||
.append((new DecimalFormat("0.00%")).format(completion));
|
||||
else
|
||||
buf.append(" <img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("head_rx.png\" > ")
|
||||
.append(_("Complete"));
|
||||
// else unknown
|
||||
buf.append(" <img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("head_rx.png\" > <b>")
|
||||
.append(_("Complete")).append("</b>");
|
||||
// up ratio
|
||||
buf.append(" <img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("head_tx.png\" > <b>")
|
||||
.append(_("Upload ratio"))
|
||||
.append(":</b> ");
|
||||
long uploaded = snark.getUploaded();
|
||||
if (uploaded > 0) {
|
||||
double ratio = uploaded / ((double) snark.getTotalLength());
|
||||
buf.append((new DecimalFormat("0.000")).format(ratio));
|
||||
buf.append(" x");
|
||||
} else {
|
||||
buf.append('0');
|
||||
}
|
||||
long needed = snark.getNeededLength();
|
||||
if (needed > 0)
|
||||
buf.append(" <img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("head_rx.png\" > <b>")
|
||||
@ -2781,19 +2965,19 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (showPriority) {
|
||||
buf.append("<td class=\"priority\">");
|
||||
if ((!complete) && (!item.isDirectory())) {
|
||||
buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(fileIndex).append("\" ");
|
||||
buf.append("\n<input type=\"radio\" onclick=\"priorityclicked();\" class=\"prihigh\" value=\"5\" name=\"pri.").append(fileIndex).append("\" ");
|
||||
if (priority > 0)
|
||||
buf.append("checked=\"true\"");
|
||||
buf.append("checked=\"checked\"");
|
||||
buf.append('>').append(_("High"));
|
||||
|
||||
buf.append("<input type=\"radio\" value=\"0\" name=\"pri.").append(fileIndex).append("\" ");
|
||||
buf.append("\n<input type=\"radio\" onclick=\"priorityclicked();\" class=\"prinorm\" value=\"0\" name=\"pri.").append(fileIndex).append("\" ");
|
||||
if (priority == 0)
|
||||
buf.append("checked=\"true\"");
|
||||
buf.append("checked=\"checked\"");
|
||||
buf.append('>').append(_("Normal"));
|
||||
|
||||
buf.append("<input type=\"radio\" value=\"-9\" name=\"pri.").append(fileIndex).append("\" ");
|
||||
buf.append("\n<input type=\"radio\" onclick=\"priorityclicked();\" class=\"priskip\" value=\"-9\" name=\"pri.").append(fileIndex).append("\" ");
|
||||
if (priority < 0)
|
||||
buf.append("checked=\"true\"");
|
||||
buf.append("checked=\"checked\"");
|
||||
buf.append('>').append(_("Skip"));
|
||||
showSaveButton = true;
|
||||
}
|
||||
@ -2802,9 +2986,16 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("</TR>\n");
|
||||
}
|
||||
if (showSaveButton) {
|
||||
buf.append("<thead><tr><th colspan=\"4\"> </th><th class=\"headerpriority\"><input type=\"submit\" value=\"");
|
||||
buf.append(_("Save priorities"));
|
||||
buf.append("\" name=\"foo\" ></th></tr></thead>\n");
|
||||
buf.append("<thead><tr><th colspan=\"4\"> </th><th class=\"headerpriority\">" +
|
||||
"<a class=\"control\" id=\"setallhigh\" href=\"javascript:void(null);\" onclick=\"setallhigh();\">")
|
||||
.append(toImg("clock_red")).append(_("Set all high")).append("</a>\n" +
|
||||
"<a class=\"control\" id=\"setallnorm\" href=\"javascript:void(null);\" onclick=\"setallnorm();\">")
|
||||
.append(toImg("clock")).append(_("Set all normal")).append("</a>\n" +
|
||||
"<a class=\"control\" id=\"setallskip\" href=\"javascript:void(null);\" onclick=\"setallskip();\">")
|
||||
.append(toImg("cancel")).append(_("Skip all")).append("</a>\n" +
|
||||
"<br><br><input type=\"submit\" class=\"accept\" value=\"").append(_("Save priorities"))
|
||||
.append("\" name=\"savepri\" >\n" +
|
||||
"</th></tr></thead>\n");
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
if (showPriority)
|
||||
@ -2814,7 +3005,12 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** @since 0.7.14 */
|
||||
/**
|
||||
* Pick an icon; try to catch the common types in an i2p environment.
|
||||
*
|
||||
* @return file name not including ".png"
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String toIcon(File item) {
|
||||
if (item.isDirectory())
|
||||
return "folder";
|
||||
@ -2823,10 +3019,12 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
|
||||
/**
|
||||
* Pick an icon; try to catch the common types in an i2p environment
|
||||
* Pkg private for FileTypeSorter.
|
||||
*
|
||||
* @return file name not including ".png"
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String toIcon(String path) {
|
||||
String toIcon(String path) {
|
||||
String icon;
|
||||
// Note that for this to work well, our custom mime.properties file must be loaded.
|
||||
String plc = path.toLowerCase(Locale.US);
|
||||
@ -2873,12 +3071,12 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
|
||||
/** @since 0.7.14 */
|
||||
private String toImg(String icon) {
|
||||
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"" + _contextPath + "/.icons/" + icon + ".png\">";
|
||||
return toImg(icon, "");
|
||||
}
|
||||
|
||||
/** @since 0.8.2 */
|
||||
private String toImg(String icon, String altText) {
|
||||
return "<img alt=\"" + altText + "\" height=\"16\" width=\"16\" src=\"" + _contextPath + "/.icons/" + icon + ".png\">";
|
||||
return "<img alt=\"" + altText + "\" height=\"16\" width=\"16\" src=\"" + _contextPath + WARBASE + "icons/" + icon + ".png\">";
|
||||
}
|
||||
|
||||
/** @since 0.8.1 */
|
||||
|
341
apps/i2psnark/java/src/org/klomp/snark/web/Sorters.java
Normal file
341
apps/i2psnark/java/src/org/klomp/snark/web/Sorters.java
Normal file
@ -0,0 +1,341 @@
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.Collator;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.klomp.snark.MetaInfo;
|
||||
import org.klomp.snark.Snark;
|
||||
|
||||
/**
|
||||
* Comparators for various columns
|
||||
*
|
||||
* @since 0.9.16 from TorrentNameComparator, moved from I2PSnarkservlet
|
||||
*/
|
||||
class Sorters {
|
||||
|
||||
/**
|
||||
* Negative is reverse
|
||||
*
|
||||
*<ul>
|
||||
*<li>0, 1: Name
|
||||
*<li>2: Status
|
||||
*<li>3: Peers
|
||||
*<li>4: ETA
|
||||
*<li>5: Size
|
||||
*<li>6: Downloaded
|
||||
*<li>7: Uploaded
|
||||
*<li>8: Down rate
|
||||
*<li>9: Up rate
|
||||
*<li>10: Remaining (needed)
|
||||
*<li>11: Upload ratio
|
||||
*<li>11: File type
|
||||
*</ul>
|
||||
*
|
||||
* @param servlet for file type callback only
|
||||
*/
|
||||
public static Comparator<Snark> getComparator(int type, I2PSnarkServlet servlet) {
|
||||
boolean rev = type < 0;
|
||||
Comparator<Snark> rv;
|
||||
switch (type) {
|
||||
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
default:
|
||||
rv = new TorrentNameComparator();
|
||||
if (rev)
|
||||
rv = Collections.reverseOrder(rv);
|
||||
break;
|
||||
|
||||
case -2:
|
||||
case 2:
|
||||
rv = new StatusComparator(rev);
|
||||
break;
|
||||
|
||||
case -3:
|
||||
case 3:
|
||||
rv = new PeersComparator(rev);
|
||||
break;
|
||||
|
||||
case -4:
|
||||
case 4:
|
||||
rv = new ETAComparator(rev);
|
||||
break;
|
||||
|
||||
case -5:
|
||||
case 5:
|
||||
rv = new SizeComparator(rev);
|
||||
break;
|
||||
|
||||
case -6:
|
||||
case 6:
|
||||
rv = new DownloadedComparator(rev);
|
||||
break;
|
||||
|
||||
case -7:
|
||||
case 7:
|
||||
rv = new UploadedComparator(rev);
|
||||
break;
|
||||
|
||||
case -8:
|
||||
case 8:
|
||||
rv = new DownRateComparator(rev);
|
||||
break;
|
||||
|
||||
case -9:
|
||||
case 9:
|
||||
rv = new UpRateComparator(rev);
|
||||
break;
|
||||
|
||||
case -10:
|
||||
case 10:
|
||||
rv = new RemainingComparator(rev);
|
||||
break;
|
||||
|
||||
case -11:
|
||||
case 11:
|
||||
rv = new RatioComparator(rev);
|
||||
break;
|
||||
|
||||
case -12:
|
||||
case 12:
|
||||
rv = new FileTypeComparator(rev, servlet);
|
||||
break;
|
||||
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort alphabetically in current locale, ignore case, ignore leading "the "
|
||||
* (I guess this is worth it, a lot of torrents start with "The "
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private static class TorrentNameComparator implements Comparator<Snark>, Serializable {
|
||||
|
||||
public int compare(Snark l, Snark r) {
|
||||
return comp(l, r);
|
||||
}
|
||||
|
||||
public static int comp(Snark l, Snark r) {
|
||||
// put downloads and magnets first
|
||||
if (l.getStorage() == null && r.getStorage() != null)
|
||||
return -1;
|
||||
if (l.getStorage() != null && r.getStorage() == null)
|
||||
return 1;
|
||||
String ls = l.getBaseName();
|
||||
String llc = ls.toLowerCase(Locale.US);
|
||||
if (llc.startsWith("the ") || llc.startsWith("the.") || llc.startsWith("the_"))
|
||||
ls = ls.substring(4);
|
||||
String rs = r.getBaseName();
|
||||
String rlc = rs.toLowerCase(Locale.US);
|
||||
if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_"))
|
||||
rs = rs.substring(4);
|
||||
return Collator.getInstance().compare(ls, rs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward or reverse sort, but the fallback is always forward
|
||||
*/
|
||||
private static abstract class Sort implements Comparator<Snark>, Serializable {
|
||||
|
||||
private final boolean _rev;
|
||||
|
||||
public Sort(boolean rev) {
|
||||
_rev = rev;
|
||||
}
|
||||
|
||||
public int compare(Snark l, Snark r) {
|
||||
int rv = compareIt(l, r);
|
||||
if (rv != 0)
|
||||
return _rev ? 0 - rv : rv;
|
||||
return TorrentNameComparator.comp(l, r);
|
||||
}
|
||||
|
||||
protected abstract int compareIt(Snark l, Snark r);
|
||||
|
||||
protected static int compLong(long l, long r) {
|
||||
if (l < r)
|
||||
return -1;
|
||||
if (l > r)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class StatusComparator extends Sort {
|
||||
|
||||
private StatusComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
int rv = getStatus(l) - getStatus(r);
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
// use reverse remaining as first tie break
|
||||
return compLong(r.getNeededLength(), l.getNeededLength());
|
||||
}
|
||||
|
||||
private static int getStatus(Snark snark) {
|
||||
long remaining = snark.getRemainingLength();
|
||||
long needed = snark.getNeededLength();
|
||||
if (snark.isStopped()) {
|
||||
if (remaining < 0)
|
||||
return 0;
|
||||
if (remaining > 0)
|
||||
return 5;
|
||||
return 10;
|
||||
}
|
||||
if (snark.isStarting())
|
||||
return 15;
|
||||
if (snark.isAllocating())
|
||||
return 20;
|
||||
if (remaining < 0)
|
||||
return 15; // magnet
|
||||
if (remaining == 0)
|
||||
return 100;
|
||||
if (snark.isChecking())
|
||||
return 95;
|
||||
if (snark.getNeededLength() <= 0)
|
||||
return 90;
|
||||
if (snark.getPeerCount() <= 0)
|
||||
return 40;
|
||||
if (snark.getDownloadRate() <= 0)
|
||||
return 50;
|
||||
return 60;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PeersComparator extends Sort {
|
||||
|
||||
public PeersComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return l.getPeerCount() - r.getPeerCount();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RemainingComparator extends Sort {
|
||||
|
||||
public RemainingComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getNeededLength(), r.getNeededLength());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ETAComparator extends Sort {
|
||||
|
||||
public ETAComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(eta(l), eta(r));
|
||||
}
|
||||
|
||||
private static long eta(Snark snark) {
|
||||
long needed = snark.getNeededLength();
|
||||
if (needed <= 0)
|
||||
return 0;
|
||||
long total = snark.getTotalLength();
|
||||
if (needed > total)
|
||||
needed = total;
|
||||
long downBps = snark.getDownloadRate();
|
||||
if (downBps > 0)
|
||||
return needed / downBps;
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SizeComparator extends Sort {
|
||||
|
||||
public SizeComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getTotalLength(), r.getTotalLength());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DownloadedComparator extends Sort {
|
||||
|
||||
public DownloadedComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
long ld = l.getTotalLength() - l.getRemainingLength();
|
||||
long rd = r.getTotalLength() - r.getRemainingLength();
|
||||
return compLong(ld, rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static class UploadedComparator extends Sort {
|
||||
|
||||
public UploadedComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getUploaded(), r.getUploaded());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DownRateComparator extends Sort {
|
||||
|
||||
public DownRateComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getDownloadRate(), r.getDownloadRate());
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpRateComparator extends Sort {
|
||||
|
||||
public UpRateComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
return compLong(l.getUploadRate(), r.getUploadRate());
|
||||
}
|
||||
}
|
||||
|
||||
private static class RatioComparator extends Sort {
|
||||
|
||||
private static final long M = 128 * 1024 * 1024;
|
||||
|
||||
public RatioComparator(boolean rev) { super(rev); }
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
long lt = l.getTotalLength();
|
||||
long ld = lt > 0 ? ((M * l.getUploaded()) / lt) : 0;
|
||||
long rt = r.getTotalLength();
|
||||
long rd = rt > 0 ? ((M * r.getUploaded()) / rt) : 0;
|
||||
return compLong(ld, rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FileTypeComparator extends Sort {
|
||||
|
||||
private final I2PSnarkServlet servlet;
|
||||
|
||||
public FileTypeComparator(boolean rev, I2PSnarkServlet servlet) {
|
||||
super(rev);
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
public int compareIt(Snark l, Snark r) {
|
||||
String ls = toName(l);
|
||||
String rs = toName(r);
|
||||
return ls.compareTo(rs);
|
||||
}
|
||||
|
||||
private String toName(Snark snark) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta == null)
|
||||
return "0";
|
||||
if (meta.getFiles() != null)
|
||||
return "1";
|
||||
// arbitrary sort based on icon name
|
||||
return servlet.toIcon(meta.getName());
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user