i2psnark:

- Consolidate and clean up parameters code
 - Click to sort by column
This commit is contained in:
zzz
2014-09-10 23:28:41 +00:00
parent 41c2c60ab0
commit 9985a02efc
2 changed files with 431 additions and 119 deletions

View File

@ -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("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'");
resp.setHeader("X-XSS-Protection", "1; mode=block"); resp.setHeader("X-XSS-Protection", "1; mode=block");
String peerParam = req.getParameter("p"); String pOverride = _manager.util().connected() ? null : "";
String stParam = req.getParameter("st"); String peerString = getQueryString(req, pOverride, null, null);
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;
}
// AJAX for mainsection // AJAX for mainsection
if ("/.ajax/xhr1.html".equals(path)) { if ("/.ajax/xhr1.html".equals(path)) {
@ -292,6 +278,7 @@ public class I2PSnarkServlet extends BasicServlet {
out.write(_("Configuration")); out.write(_("Configuration"));
else else
out.write(_("Anonymous BitTorrent Client")); out.write(_("Anonymous BitTorrent Client"));
String peerParam = req.getParameter("p");
if ("2".equals(peerParam)) if ("2".equals(peerParam))
out.write(" | Debug Mode"); out.write(" | Debug Mode");
out.write("</title>\n"); out.write("</title>\n");
@ -413,13 +400,7 @@ public class I2PSnarkServlet extends BasicServlet {
boolean isForm = _manager.util().connected() || !snarks.isEmpty(); boolean isForm = _manager.util().connected() || !snarks.isEmpty();
if (isForm) { if (isForm) {
out.write("<form action=\"_post\" method=\"POST\">\n"); out.write("<form action=\"_post\" method=\"POST\">\n");
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n"); writeHiddenInputs(out, req, null);
// 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");
} }
out.write(TABLE_HEADER); out.write(TABLE_HEADER);
@ -441,18 +422,19 @@ public class I2PSnarkServlet extends BasicServlet {
} }
int pageSize = Math.max(_manager.getPageSize(), 5); int pageSize = Math.max(_manager.getPageSize(), 5);
out.write("<tr><th><img border=\"0\" src=\"" + _imgPath + "status.png\" title=\""); String currentSort = req.getParameter("sort");
out.write("<tr><th>");
String sort = ("2".equals(currentSort)) ? "-2" : "2";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\"><img border=\"0\" src=\"" + _imgPath + "status.png\" title=\"");
out.write(_("Status")); out.write(_("Status"));
out.write("\" alt=\""); out.write("\" alt=\"");
out.write(_("Status")); out.write(_("Status"));
out.write("\"></th>\n<th>"); out.write("\"></a></th>\n<th>");
if (_manager.util().connected() && !snarks.isEmpty()) { if (_manager.util().connected() && !snarks.isEmpty()) {
out.write(" <a href=\"" + _contextPath + '/'); out.write(" <a href=\"" + _contextPath + '/');
if (peerParam != null) { if (peerParam != null) {
if (stParam != null) { // disable peer view
out.write("?st=");
out.write(stParam);
}
out.write("\">"); out.write("\">");
out.write("<img border=\"0\" src=\"" + _imgPath + "hidepeers.png\" title=\""); out.write("<img border=\"0\" src=\"" + _imgPath + "hidepeers.png\" title=\"");
out.write(_("Hide Peers")); out.write(_("Hide Peers"));
@ -460,11 +442,8 @@ public class I2PSnarkServlet extends BasicServlet {
out.write(_("Hide Peers")); out.write(_("Hide Peers"));
out.write("\">"); out.write("\">");
} else { } else {
out.write("?p=1"); // enable peer view
if (stParam != null) { out.write(getQueryString(req, "1", null, null));
out.write("&amp;st=");
out.write(stParam);
}
out.write("\">"); out.write("\">");
out.write("<img border=\"0\" src=\"" + _imgPath + "showpeers.png\" title=\""); out.write("<img border=\"0\" src=\"" + _imgPath + "showpeers.png\" title=\"");
out.write(_("Show Peers")); out.write(_("Show Peers"));
@ -475,56 +454,69 @@ public class I2PSnarkServlet extends BasicServlet {
out.write("</a><br>\n"); out.write("</a><br>\n");
} }
out.write("</th>\n<th colspan=\"2\" align=\"left\">"); out.write("</th>\n<th colspan=\"2\" align=\"left\">");
out.write("<img border=\"0\" src=\"" + _imgPath + "torrent.png\" title=\""); sort = (currentSort == null || "0".equals(currentSort) || "1".equals(currentSort)) ? "-1" : "";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\"><img border=\"0\" src=\"" + _imgPath + "torrent.png\" title=\"");
out.write(_("Torrent")); out.write(_("Torrent"));
out.write("\" alt=\""); out.write("\" alt=\"");
out.write(_("Torrent")); out.write(_("Torrent"));
out.write("\"></th>\n<th align=\"center\">"); out.write("\"></a></th>\n<th align=\"center\">");
if (total > 0 && (start > 0 || total > pageSize)) { 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\">"); out.write("</th>\n<th align=\"right\">");
if (_manager.util().connected() && !snarks.isEmpty()) { if (_manager.util().connected() && !snarks.isEmpty()) {
out.write("<img border=\"0\" src=\"" + _imgPath + "eta.png\" title=\""); sort = ("4".equals(currentSort)) ? "-4" : "4";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\"><img border=\"0\" src=\"" + _imgPath + "eta.png\" title=\"");
out.write(_("Estimated time remaining")); out.write(_("Estimated time remaining"));
out.write("\" alt=\""); out.write("\" alt=\"");
// Translators: Please keep short or translate as " " // Translators: Please keep short or translate as " "
out.write(_("ETA")); out.write(_("ETA"));
out.write("\">"); out.write("\"></a>");
} }
out.write("</th>\n<th align=\"right\">"); out.write("</th>\n<th align=\"right\">");
out.write("<img border=\"0\" src=\"" + _imgPath + "head_rx.png\" title=\""); // sort by size, not downloaded
sort = ("5".equals(currentSort)) ? "-5" : "5";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\"><img border=\"0\" src=\"" + _imgPath + "head_rx.png\" title=\"");
out.write(_("Downloaded")); out.write(_("Downloaded"));
out.write("\" alt=\""); out.write("\" alt=\"");
// Translators: Please keep short or translate as " " // Translators: Please keep short or translate as " "
out.write(_("RX")); out.write(_("RX"));
out.write("\">"); out.write("\"></a>");
out.write("</th>\n<th align=\"right\">"); out.write("</th>\n<th align=\"right\">");
if (!snarks.isEmpty()) { if (!snarks.isEmpty()) {
out.write("<img border=\"0\" src=\"" + _imgPath + "head_tx.png\" title=\""); sort = ("7".equals(currentSort)) ? "-7" : "7";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\"><img border=\"0\" src=\"" + _imgPath + "head_tx.png\" title=\"");
out.write(_("Uploaded")); out.write(_("Uploaded"));
out.write("\" alt=\""); out.write("\" alt=\"");
// Translators: Please keep short or translate as " " // Translators: Please keep short or translate as " "
out.write(_("TX")); out.write(_("TX"));
out.write("\">"); out.write("\"></a>");
} }
out.write("</th>\n<th align=\"right\">"); out.write("</th>\n<th align=\"right\">");
if (_manager.util().connected() && !snarks.isEmpty()) { if (_manager.util().connected() && !snarks.isEmpty()) {
out.write("<img border=\"0\" src=\"" + _imgPath + "head_rxspeed.png\" title=\""); sort = ("8".equals(currentSort)) ? "-8" : "8";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\"><img border=\"0\" src=\"" + _imgPath + "head_rxspeed.png\" title=\"");
out.write(_("Down Rate")); out.write(_("Down Rate"));
out.write("\" alt=\""); out.write("\" alt=\"");
// Translators: Please keep short or translate as " " // Translators: Please keep short or translate as " "
out.write(_("RX Rate")); out.write(_("RX Rate"));
out.write(" \">"); out.write("\"></a>");
} }
out.write("</th>\n<th align=\"right\">"); out.write("</th>\n<th align=\"right\">");
if (_manager.util().connected() && !snarks.isEmpty()) { if (_manager.util().connected() && !snarks.isEmpty()) {
out.write("<img border=\"0\" src=\"" + _imgPath + "head_txspeed.png\" title=\""); sort = ("9".equals(currentSort)) ? "-9" : "9";
out.write("<a href=\"" + _contextPath + '/' + getQueryString(req, null, null, sort));
out.write("\"><img border=\"0\" src=\"" + _imgPath + "head_txspeed.png\" title=\"");
out.write(_("Up Rate")); out.write(_("Up Rate"));
out.write("\" alt=\""); out.write("\" alt=\"");
// Translators: Please keep short or translate as " " // Translators: Please keep short or translate as " "
out.write(_("TX Rate")); out.write(_("TX Rate"));
out.write(" \">"); out.write("\"></a>");
} }
out.write("</th>\n<th align=\"center\">"); out.write("</th>\n<th align=\"center\">");
@ -580,12 +572,11 @@ public class I2PSnarkServlet extends BasicServlet {
String uri = _contextPath + '/'; String uri = _contextPath + '/';
boolean showDebug = "2".equals(peerParam); boolean showDebug = "2".equals(peerParam);
String stParamStr = stParam == null ? "" : "&amp;st=" + stParam;
for (int i = 0; i < total; i++) { for (int i = 0; i < total; i++) {
Snark snark = snarks.get(i); Snark snark = snarks.get(i);
boolean showPeers = showDebug || "1".equals(peerParam) || Base64.encode(snark.getInfoHash()).equals(peerParam); boolean showPeers = showDebug || "1".equals(peerParam) || Base64.encode(snark.getInfoHash()).equals(peerParam);
boolean hide = i < start || i >= start + pageSize; 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);
} }
if (total == 0) { if (total == 0) {
@ -636,17 +627,105 @@ public class I2PSnarkServlet extends BasicServlet {
return start == 0; 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("&amp;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("&amp;st=");
buf.append(st);
}
return buf.toString();
}
/** /**
* @since 0.9.6 * @since 0.9.6
*/ */
private void writePageNav(PrintWriter out, int start, int pageSize, int total, private void writePageNav(PrintWriter out, HttpServletRequest req, int start, int pageSize, int total,
String peerParam, boolean noThinsp) { boolean noThinsp) {
// Page nav // Page nav
if (start > 0) { if (start > 0) {
// First // First
out.write("<a href=\"" + _contextPath); out.write("<a href=\"" + _contextPath);
if (peerParam != null) out.write(getQueryString(req, null, "", null));
out.write("?p=" + peerParam);
out.write("\">" + out.write("\">" +
"<img alt=\"" + _("First") + "\" title=\"" + _("First page") + "\" border=\"0\" src=\"" + "<img alt=\"" + _("First") + "\" title=\"" + _("First page") + "\" border=\"0\" src=\"" +
_imgPath + "control_rewind_blue.png\">" + _imgPath + "control_rewind_blue.png\">" +
@ -655,9 +734,9 @@ public class I2PSnarkServlet extends BasicServlet {
//if (prev > 0) { //if (prev > 0) {
if (true) { if (true) {
// Back // Back
out.write("&nbsp;<a href=\"" + _contextPath + "?st=" + prev); out.write("&nbsp;<a href=\"" + _contextPath);
if (peerParam != null) String sprev = (prev > 0) ? Integer.toString(prev) : "";
out.write("&amp;p=" + peerParam); out.write(getQueryString(req, null, sprev, null));
out.write("\">" + out.write("\">" +
"<img alt=\"" + _("Prev") + "\" title=\"" + _("Previous page") + "\" border=\"0\" src=\"" + "<img alt=\"" + _("Prev") + "\" title=\"" + _("Previous page") + "\" border=\"0\" src=\"" +
_imgPath + "control_back_blue.png\">" + _imgPath + "control_back_blue.png\">" +
@ -690,9 +769,8 @@ public class I2PSnarkServlet extends BasicServlet {
//if (next + pageSize < total) { //if (next + pageSize < total) {
if (true) { if (true) {
// Next // Next
out.write("&nbsp;<a href=\"" + _contextPath + "?st=" + next); out.write("&nbsp;<a href=\"" + _contextPath);
if (peerParam != null) out.write(getQueryString(req, null, Integer.toString(next), null));
out.write("&amp;p=" + peerParam);
out.write("\">" + out.write("\">" +
"<img alt=\"" + _("Next") + "\" title=\"" + _("Next page") + "\" border=\"0\" src=\"" + "<img alt=\"" + _("Next") + "\" title=\"" + _("Next page") + "\" border=\"0\" src=\"" +
_imgPath + "control_play_blue.png\">" + _imgPath + "control_play_blue.png\">" +
@ -700,9 +778,8 @@ public class I2PSnarkServlet extends BasicServlet {
} }
// Last // Last
int last = ((total - 1) / pageSize) * pageSize; int last = ((total - 1) / pageSize) * pageSize;
out.write("&nbsp;<a href=\"" + _contextPath + "?st=" + last); out.write("&nbsp;<a href=\"" + _contextPath);
if (peerParam != null) out.write(getQueryString(req, null, Integer.toString(last), null));
out.write("&amp;p=" + peerParam);
out.write("\">" + out.write("\">" +
"<img alt=\"" + _("Last") + "\" title=\"" + _("Last page") + "\" border=\"0\" src=\"" + "<img alt=\"" + _("Last") + "\" title=\"" + _("Last page") + "\" border=\"0\" src=\"" +
_imgPath + "control_fastforward_blue.png\">" + _imgPath + "control_fastforward_blue.png\">" +
@ -1190,34 +1267,16 @@ public class I2PSnarkServlet extends BasicServlet {
return buf.toString(); 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) { private List<Snark> getSortedSnarks(HttpServletRequest req) {
ArrayList<Snark> rv = new ArrayList<Snark>(_manager.getTorrents()); ArrayList<Snark> rv = new ArrayList<Snark>(_manager.getTorrents());
Collections.sort(rv, new TorrentNameComparator()); int sort = 0;
String ssort = req.getParameter("sort");
if (ssort != null) {
try {
sort = Integer.parseInt(ssort);
} catch (NumberFormatException nfe) {}
}
Collections.sort(rv, Sorters.getComparator(sort));
return rv; return rv;
} }
@ -1229,11 +1288,11 @@ public class I2PSnarkServlet extends BasicServlet {
* *
* @param stats in/out param (totals) * @param stats in/out param (totals)
* @param statsOnly if true, output nothing, update stats only * @param statsOnly if true, output nothing, update stats only
* @param stParam non null; empty or e.g. &amp;st=10
*/ */
private void displaySnark(PrintWriter out, Snark snark, String uri, int row, long stats[], boolean showPeers, private void displaySnark(PrintWriter out, HttpServletRequest req,
boolean isDegraded, boolean noThinsp, boolean showDebug, boolean statsOnly, Snark snark, String uri, int row, long stats[], boolean showPeers,
String stParam) throws IOException { boolean isDegraded, boolean noThinsp, boolean showDebug, boolean statsOnly)
throws IOException {
// stats // stats
long uploaded = snark.getUploaded(); long uploaded = snark.getUploaded();
stats[0] += snark.getDownloaded(); stats[0] += snark.getDownloaded();
@ -1335,7 +1394,7 @@ public class I2PSnarkServlet extends BasicServlet {
if (curPeers > 0 && !showPeers) if (curPeers > 0 && !showPeers)
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + img + ".png\" title=\"" + txt + "\"></td>" + statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + img + ".png\" title=\"" + txt + "\"></td>" +
"<td class=\"snarkTorrentStatus\">" + txt + "<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) + curPeers + thinsp(noThinsp) +
ngettext("1 peer", "{0} peers", knownPeers) + "</a>"; ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
else else
@ -1351,7 +1410,7 @@ public class I2PSnarkServlet extends BasicServlet {
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers) if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "downloading.png\" title=\"" + _("OK") + "\"></td>" + statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "downloading.png\" title=\"" + _("OK") + "\"></td>" +
"<td class=\"snarkTorrentStatus\">" + _("OK") + "<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) + curPeers + thinsp(noThinsp) +
ngettext("1 peer", "{0} peers", knownPeers) + "</a>"; ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
else if (isRunning && curPeers > 0 && downBps > 0) else if (isRunning && curPeers > 0 && downBps > 0)
@ -1362,7 +1421,7 @@ public class I2PSnarkServlet extends BasicServlet {
else if (isRunning && curPeers > 0 && !showPeers) else if (isRunning && curPeers > 0 && !showPeers)
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Stalled") + "\"></td>" + statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Stalled") + "\"></td>" +
"<td class=\"snarkTorrentStatus\">" + _("Stalled") + "<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) + curPeers + thinsp(noThinsp) +
ngettext("1 peer", "{0} peers", knownPeers) + "</a>"; ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
else if (isRunning && curPeers > 0) else if (isRunning && curPeers > 0)
@ -1484,7 +1543,8 @@ public class I2PSnarkServlet extends BasicServlet {
} else if (isRunning) { } else if (isRunning) {
// Stop Button // Stop Button
if (isDegraded) if (isDegraded)
out.write("<a href=\"" + _contextPath + "/?action=Stop_" + b64 + "&amp;nonce=" + _nonce + stParam + "\"><img title=\""); out.write("<a href=\"" + _contextPath + "/?action=Stop_" + b64 + "&amp;nonce=" + _nonce +
getQueryString(req, "", null, null).replace("?", "&amp;") + "\"><img title=\"");
else else
out.write("<input type=\"image\" name=\"action_Stop_" + b64 + "\" value=\"foo\" title=\""); out.write("<input type=\"image\" name=\"action_Stop_" + b64 + "\" value=\"foo\" title=\"");
out.write(_("Stop the torrent")); out.write(_("Stop the torrent"));
@ -1498,7 +1558,8 @@ public class I2PSnarkServlet extends BasicServlet {
// Start Button // Start Button
// This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent // This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent
if (noThinsp) if (noThinsp)
out.write("<a href=\"" + _contextPath + "/?action=Start_" + b64 + "&amp;nonce=" + _nonce + stParam + "\"><img title=\""); out.write("<a href=\"" + _contextPath + "/?action=Start_" + b64 + "&amp;nonce=" + _nonce +
getQueryString(req, "", null, null).replace("?", "&amp;") + "\"><img title=\"");
else else
out.write("<input type=\"image\" name=\"action_Start_" + b64 + "\" value=\"foo\" title=\""); out.write("<input type=\"image\" name=\"action_Start_" + b64 + "\" value=\"foo\" title=\"");
out.write(_("Start the torrent")); out.write(_("Start the torrent"));
@ -1512,7 +1573,8 @@ public class I2PSnarkServlet extends BasicServlet {
// Remove Button // Remove Button
// Doesnt work with Opera so use noThinsp instead of isDegraded // Doesnt work with Opera so use noThinsp instead of isDegraded
if (noThinsp) if (noThinsp)
out.write("<a href=\"" + _contextPath + "/?action=Remove_" + b64 + "&amp;nonce=" + _nonce + stParam + "\"><img title=\""); out.write("<a href=\"" + _contextPath + "/?action=Remove_" + b64 + "&amp;nonce=" + _nonce +
getQueryString(req, "", null, null).replace("?", "&amp;") + "\"><img title=\"");
else else
out.write("<input type=\"image\" name=\"action_Remove_" + b64 + "\" value=\"foo\" title=\""); 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")); out.write(_("Remove the torrent from the active list, deleting the .torrent file"));
@ -1532,7 +1594,8 @@ public class I2PSnarkServlet extends BasicServlet {
// Delete Button // Delete Button
// Doesnt work with Opera so use noThinsp instead of isDegraded // Doesnt work with Opera so use noThinsp instead of isDegraded
if (noThinsp) if (noThinsp)
out.write("<a href=\"" + _contextPath + "/?action=Delete_" + b64 + "&amp;nonce=" + _nonce + stParam + "\"><img title=\""); out.write("<a href=\"" + _contextPath + "/?action=Delete_" + b64 + "&amp;nonce=" + _nonce +
getQueryString(req, "", null, null).replace("?", "&amp;") + "\"><img title=\"");
else else
out.write("<input type=\"image\" name=\"action_Delete_" + b64 + "\" value=\"foo\" title=\""); out.write("<input type=\"image\" name=\"action_Delete_" + b64 + "\" value=\"foo\" title=\"");
out.write(_("Delete the .torrent file and the associated data file(s)")); out.write(_("Delete the .torrent file and the associated data file(s)"));
@ -1826,12 +1889,7 @@ public class I2PSnarkServlet extends BasicServlet {
out.write("<div class=\"snarkNewTorrent\">\n"); out.write("<div class=\"snarkNewTorrent\">\n");
// *not* enctype="multipart/form-data", so that the input type=file sends the filename, not the file // *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("<form action=\"_post\" method=\"POST\">\n");
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n"); writeHiddenInputs(out, req, "Add");
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");
out.write("<div class=\"addtorrentsection\"><span class=\"snarkConfigTitle\">"); out.write("<div class=\"addtorrentsection\"><span class=\"snarkConfigTitle\">");
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "add.png\"> "); out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "add.png\"> ");
out.write(_("Add Torrent")); out.write(_("Add Torrent"));
@ -1858,12 +1916,7 @@ public class I2PSnarkServlet extends BasicServlet {
out.write("<a name=\"add\"></a><div class=\"newtorrentsection\"><div class=\"snarkNewTorrent\">\n"); 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 // *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("<form action=\"_post\" method=\"POST\">\n");
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n"); writeHiddenInputs(out, req, "Create");
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");
out.write("<span class=\"snarkConfigTitle\">"); out.write("<span class=\"snarkConfigTitle\">");
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "create.png\"> "); out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "create.png\"> ");
out.write(_("Create Torrent")); out.write(_("Create Torrent"));
@ -1929,10 +1982,9 @@ public class I2PSnarkServlet extends BasicServlet {
//int seedPct = 0; //int seedPct = 0;
out.write("<form action=\"" + _contextPath + "/configure\" method=\"POST\">\n" + out.write("<form action=\"" + _contextPath + "/configure\" method=\"POST\">\n" +
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n" + "<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n");
"<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n" + writeHiddenInputs(out, req, "Save");
"<input type=\"hidden\" name=\"action\" value=\"Save\" >\n" + out.write("<span class=\"snarkConfigTitle\">" +
"<span class=\"snarkConfigTitle\">" +
"<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> "); "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
out.write(_("Configuration")); out.write(_("Configuration"));
out.write("</span><hr>\n" + out.write("</span><hr>\n" +
@ -2115,10 +2167,9 @@ public class I2PSnarkServlet extends BasicServlet {
private void writeTrackerForm(PrintWriter out, HttpServletRequest req) throws IOException { private void writeTrackerForm(PrintWriter out, HttpServletRequest req) throws IOException {
StringBuilder buf = new StringBuilder(1024); StringBuilder buf = new StringBuilder(1024);
buf.append("<form action=\"" + _contextPath + "/configure\" method=\"POST\">\n" + buf.append("<form action=\"" + _contextPath + "/configure\" method=\"POST\">\n" +
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n" + "<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n");
"<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n" + writeHiddenInputs(buf, req, "Save2");
"<input type=\"hidden\" name=\"action\" value=\"Save2\" >\n" + buf.append("<span class=\"snarkConfigTitle\">" +
"<span class=\"snarkConfigTitle\">" +
"<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> "); "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
buf.append(_("Trackers")); buf.append(_("Trackers"));
buf.append("</span><hr>\n" + buf.append("</span><hr>\n" +

View File

@ -0,0 +1,261 @@
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.Snark;
/**
* Comparators for various columns
*
* @since 0.9.16 moved from I2PSnarkservlet
*/
class Sorters {
public static Comparator<Snark> getComparator(int type) {
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;
}
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) {
return getStatus(l) - getStatus(r);
}
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();
long total = snark.getTotalLength();
if (needed > total)
needed = total;
long remainingSeconds;
long downBps = snark.getDownloadRate();
if (downBps > 0 && needed > 0)
return needed / downBps;
return -1;
}
}
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) {
return compLong(l.getDownloaded(), r.getDownloaded());
}
}
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());
}
}
}