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");