diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
index fb53c3190..25856facd 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -214,7 +214,7 @@ public class I2PSnarkServlet extends BasicServlet {
req.setCharacterEncoding("UTF-8");
String pOverride = _manager.util().connected() ? null : "";
- String peerString = getQueryString(req, pOverride, null, null);
+ String peerString = getQueryString(req, pOverride, null, null, "");
String cspNonce = Integer.toHexString(_context.random().nextInt());
// AJAX for mainsection
@@ -378,9 +378,19 @@ public class I2PSnarkServlet extends BasicServlet {
continue;
if (_manager.util().isKnownOpenTracker(t.announceURL))
continue;
- out.write(" " + t.name + "");
+ out.write(" " + t.name + "\n");
}
}
+ if (_manager.getTorrents().size() > 1) {
+ out.write("
\n");
+ }
}
out.write("\n");
String newURL = req.getParameter("newURL");
@@ -488,6 +498,17 @@ public class I2PSnarkServlet extends BasicServlet {
boolean isDegraded = ua != null && ServletUtil.isTextBrowser(ua);
boolean noThinsp = isDegraded || (ua != null && ua.startsWith("Opera"));
+ // search
+ boolean isSearch = false;
+ String search = req.getParameter("s");
+ if (search != null && search.length() > 0) {
+ List matches = search(search, snarks);
+ if (matches != null) {
+ snarks = matches;
+ isSearch = true;
+ }
+ }
+
// pages
int start = 0;
int total = snarks.size();
@@ -743,6 +764,8 @@ public class I2PSnarkServlet extends BasicServlet {
out.write(_t("Unreadable") + ": " + DataHelper.escapeHTML(dd.toString()));
} else if (!canWrite) {
out.write(_t("No write permissions for data directory") + ": " + DataHelper.escapeHTML(dd.toString()));
+ } else if (isSearch) {
+ out.write(_t("No torrents found."));
} else {
out.write(_t("No torrents loaded."));
}
@@ -817,6 +840,43 @@ public class I2PSnarkServlet extends BasicServlet {
return start == 0;
}
+
+ /**
+ * search torrents for matching terms
+ *
+ * @param search non-null
+ * @param snarks unmodified
+ * @return null if no valid search, or matching torrents in same order, empty if no match
+ * @since 0.9.58
+ */
+ private static List search(String search, Collection snarks) {
+ List searchList = null;
+ String[] terms = DataHelper.split(search, " ");
+ for (int i = 0; i < terms.length; i++) {
+ String term = terms[i];
+ if (term.length() > 0) {
+ if (searchList == null)
+ searchList = new ArrayList(4);
+ searchList.add(term.toLowerCase(Locale.US));
+ }
+ }
+ if (searchList == null)
+ return null;
+ List matches = new ArrayList(32);
+ for (Snark snark : snarks) {
+ String lcname = snark.getBaseName().toLowerCase(Locale.US);
+ // search for any term (OR)
+ for (int j = 0; j < searchList.size(); j++) {
+ String term = searchList.get(j);
+ if (lcname.contains(term)) {
+ matches.add(snark);
+ break;
+ }
+ }
+ }
+ return matches;
+ }
+
/**
* hidden inputs for nonce and paramters p, st, and sort
*
@@ -858,11 +918,19 @@ public class I2PSnarkServlet extends BasicServlet {
if (action != null) {
buf.append("\n");
+ } else {
+ // for buttons, keep the search term
+ String sParam = req.getParameter("s");
+ if (sParam != null) {
+ buf.append("\n");
+ }
}
}
/**
- * Build HTML-escaped and stripped query string
+ * Build HTML-escaped and stripped query string.
+ * Keeps any existing search param.
*
* @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
@@ -871,6 +939,14 @@ public class I2PSnarkServlet extends BasicServlet {
* @since 0.9.16
*/
private static String getQueryString(HttpServletRequest req, String p, String st, String so) {
+ return getQueryString(req, p, st, so, null);
+ }
+
+ /**
+ * @param s search param override or "" for default or null to keep the same as in req
+ * @since 0.9.58
+ */
+ private static String getQueryString(HttpServletRequest req, String p, String st, String so, String s) {
StringBuilder buf = new StringBuilder(64);
if (p == null) {
p = req.getParameter("p");
@@ -903,6 +979,18 @@ public class I2PSnarkServlet extends BasicServlet {
buf.append("&st=");
buf.append(st);
}
+ if (s == null) {
+ s = req.getParameter("s");
+ if (s != null)
+ s = DataHelper.escapeHTML(s);
+ }
+ if (s != null && !s.equals("")) {
+ if (buf.length() <= 0)
+ buf.append("?s=");
+ else
+ buf.append("&s=");
+ buf.append(s);
+ }
return buf.toString();
}
@@ -1460,8 +1548,34 @@ public class I2PSnarkServlet extends BasicServlet {
_manager.addMessage(_t("Error creating torrent - you must enter a file or directory"));
}
} else if ("StopAll".equals(action)) {
+ String search = req.getParameter("s");
+ if (search != null && search.length() > 0) {
+ List matches = search(search, _manager.getTorrents());
+ if (matches != null) {
+ for (Snark snark : matches) {
+ _manager.stopTorrent(snark, false);
+ }
+ return;
+ }
+ }
_manager.stopAllTorrents(false);
} else if ("StartAll".equals(action)) {
+ String search = req.getParameter("s");
+ if (search != null && search.length() > 0) {
+ List matches = search(search, _manager.getTorrents());
+ if (matches != null) {
+ // TODO thread it
+ int count = 0;
+ for (Snark snark : matches) {
+ _manager.startTorrent(snark);
+ if ((count++ & 0x0f) == 15) {
+ // try to prevent OOMs
+ try { Thread.sleep(250); } catch (InterruptedException ie) {}
+ }
+ }
+ return;
+ }
+ }
_manager.startAllTorrents();
} else if ("Clear".equals(action)) {
String sid = req.getParameter("id");