diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index c3070913e..bf869f412 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -522,6 +522,11 @@ public class PeerCoordinator implements PeerListener //Only request a piece we've requested before if there's no other choice. if (piece == null) { + // AND if there are almost no wanted pieces left (real end game). + // If we do end game all the time, we generate lots of extra traffic + // when the seeder is super-slow and all the peers are "caught up" + if (wantedPieces.size() > 4) + return -1; // nothing to request and not in end game // let's not all get on the same piece Collections.shuffle(requested); Iterator it2 = requested.iterator(); diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index 12c299840..5b7d32d0e 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -734,7 +734,7 @@ public class Snark //if (debug >= INFO && t != null) // t.printStackTrace(); stopTorrent(); - throw new RuntimeException("die bart die"); + throw new RuntimeException(s + (t == null ? "" : ": " + t.getMessage())); } /** 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 63921f7f1..dd0db4337 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.text.Collator; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.Enumeration; import java.util.Iterator; @@ -37,7 +38,10 @@ import org.klomp.snark.SnarkManager; import org.klomp.snark.Storage; import org.klomp.snark.TrackerClient; +import org.mortbay.http.HttpResponse; import org.mortbay.jetty.servlet.Default; +import org.mortbay.util.Resource; +import org.mortbay.util.URI; /** * We extend Default instead of HTTPServlet so we can handle @@ -105,13 +109,52 @@ public class I2PSnarkServlet extends Default { super.destroy(); } + /** + * Some parts modified from: + *
+      // ========================================================================
+      // $Id: Default.java,v 1.51 2006/10/08 14:13:18 gregwilkins Exp $
+      // Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
+      // ------------------------------------------------------------------------
+      // Licensed under the Apache License, Version 2.0 (the "License");
+      // you may not use this file except in compliance with the License.
+      // You may obtain a copy of the License at 
+      // http://www.apache.org/licenses/LICENSE-2.0
+      // Unless required by applicable law or agreed to in writing, software
+      // distributed under the License is distributed on an "AS IS" BASIS,
+      // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      // See the License for the specific language governing permissions and
+      // limitations under the License.
+      // ========================================================================
+     * 
+ * + */ @Override public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // this is the part after /i2psnark String path = req.getServletPath(); // index.jsp doesn't work, it is grabbed by the war handler before here if (!(path == null || path.equals("/") || path.equals("/index.jsp") || path.equals("/index.html"))) { - super.service(req, resp); + if (path.endsWith("/")) { + // bypass the horrid Resource.getListHTML() + String pathInfo = req.getPathInfo(); + String pathInContext = URI.addPaths(path, pathInfo); + resp.setCharacterEncoding("UTF-8"); + resp.setContentType("text/html; charset=UTF-8"); + Resource resource = getResource(pathInContext); + if (resource == null || (!resource.exists()) || !resource.isDirectory()) { + resp.sendError(HttpResponse.__404_Not_Found); + } else { + String base = URI.addPaths(req.getRequestURI(), "/"); + String listing = getListHTML(resource, base, true); + if (listing != null) + resp.getWriter().write(listing); + else // shouldn't happen + resp.sendError(HttpResponse.__404_Not_Found); + } + } else { + super.service(req, resp); + } return; } @@ -305,7 +348,7 @@ public class I2PSnarkServlet extends Default { } } else if (newURL != null) { if (newURL.startsWith("http://")) { - _manager.addMessage(_("Fetching {0}", newURL)); + _manager.addMessage(_("Fetching {0}", urlify(newURL))); I2PAppThread fetch = new I2PAppThread(new FetchAndAdd(_manager, newURL), "Fetch and add"); fetch.start(); } else { @@ -689,7 +732,7 @@ public class I2PSnarkServlet extends Default { out.write("\n\t"); out.write(""); if (remaining > 0) - out.write(formatSize(total-remaining) + "/" + formatSize(total)); // 18MB/3GB + out.write(formatSize(total-remaining) + " / " + formatSize(total)); // 18MB/3GB else out.write(formatSize(total)); // 3GB out.write("\n\t"); @@ -1090,18 +1133,24 @@ public class I2PSnarkServlet extends Default { } // rounding makes us look faster :) - private String formatSize(long bytes) { + private static String formatSize(long bytes) { if (bytes < 5*1024) - return bytes + "B"; + return bytes + " B"; else if (bytes < 5*1024*1024) - return ((bytes + 512)/1024) + "KB"; + return ((bytes + 512)/1024) + " KB"; else if (bytes < 10*1024*1024*1024l) - return ((bytes + 512*1024)/(1024*1024)) + "MB"; + return ((bytes + 512*1024)/(1024*1024)) + " MB"; else - return ((bytes + 512*1024*1024)/(1024*1024*1024)) + "GB"; + return ((bytes + 512*1024*1024)/(1024*1024*1024)) + " GB"; } - private static final String HEADER = ""; + private static String urlify(String s) { + StringBuilder buf = new StringBuilder(256); + buf.append("").append(s).append(""); + return buf.toString(); + } + + private static final String HEADER = ""; private static final String TABLE_HEADER = "\n" + @@ -1112,6 +1161,114 @@ public class I2PSnarkServlet extends Default { private static final String FOOTER = ""; + /** + * Modded heavily from the Jetty version in Resource.java, + * pass Resource as 1st param + * All the xxxResource constructors are package local so we can't extend them. + * + *
+      // ========================================================================
+      // $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
+      // Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
+      // ------------------------------------------------------------------------
+      // Licensed under the Apache License, Version 2.0 (the "License");
+      // you may not use this file except in compliance with the License.
+      // You may obtain a copy of the License at 
+      // http://www.apache.org/licenses/LICENSE-2.0
+      // Unless required by applicable law or agreed to in writing, software
+      // distributed under the License is distributed on an "AS IS" BASIS,
+      // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      // See the License for the specific language governing permissions and
+      // limitations under the License.
+      // ========================================================================
+     * 
+ * + * Get the resource list as a HTML directory listing. + * @param r The Resource + * @param base The base URL + * @param parent True if the parent directory should be included + * @return String of HTML + */ + private String getListHTML(Resource r, String base, boolean parent) + throws IOException + { + if (!r.isDirectory()) + return null; + + String[] ls = r.list(); + if (ls==null) + return null; + Arrays.sort(ls, Collator.getInstance()); + + StringBuilder buf=new StringBuilder(4096); + buf.append(""); + String title = URI.decodePath(base); + if (title.startsWith("/i2psnark/")) + title = title.substring("/i2psnark/".length()); + if (title.endsWith("/")) + title = title.substring(0, title.length() - 1); + title = _("Completed Torrent") + ": " + title; + buf.append(title); + buf.append("").append(HEADER).append("\n
"); + buf.append(title); + + if (parent) + { + buf.append("\n
") + .append(_("Up to higher level directory")).append("\n"); + } + + buf.append("
" + + "
" + + ""); + //DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM, + // DateFormat.MEDIUM); + for (int i=0 ; i< ls.length ; i++) + { + String encoded=URI.encodePath(ls[i]); + // bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times) + // http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs + // See resource.diff attachment + //Resource item = addPath(encoded); + Resource item = r.addPath(ls[i]); + + String rowClass = (i % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd"); + buf.append("\n"); + } + buf.append("
").append(_("File")).append("").append(_("Size")) + .append("").append(_("Status")).append("
"); + + String path=URI.addPaths(base,encoded); + if (item.isDirectory() && !path.endsWith("/")) + path=URI.addPaths(path,"/"); + // thumbnail ? + String plc = path.toLowerCase(); + if (plc.endsWith(".jpg") || plc.endsWith(".png") || plc.endsWith(".gif")) + buf.append("\"\" "); + buf.append(""); + buf.append(ls[i]); + buf.append(""); + if (!item.isDirectory()) + buf.append(DataHelper.formatSize(item.length())).append(' ').append(_("Bytes")); + buf.append(""); + //buf.append(dfmt.format(new Date(item.lastModified()))); + // TODO add real status + if (!item.isDirectory()) + buf.append(_("Complete")); + buf.append("
\n"); + buf.append("\n"); + + return buf.toString(); + } + + /** inner class, don't bother reindenting */ private static class FetchAndAdd implements Runnable { private SnarkManager _manager; @@ -1126,7 +1283,7 @@ private static class FetchAndAdd implements Runnable { File file = _manager.util().get(_url, false, 3); try { if ( (file != null) && (file.exists()) && (file.length() > 0) ) { - _manager.addMessage(_("Torrent fetched from {0}", _url)); + _manager.addMessage(_("Torrent fetched from {0}", urlify(_url))); FileInputStream in = null; try { in = new FileInputStream(file); @@ -1154,12 +1311,12 @@ private static class FetchAndAdd implements Runnable { _manager.addTorrent(canonical); } } catch (IOException ioe) { - _manager.addMessage(_("Torrent at {0} was not valid", _url) + ": " + ioe.getMessage()); + _manager.addMessage(_("Torrent at {0} was not valid", urlify(_url)) + ": " + ioe.getMessage()); } finally { try { in.close(); } catch (IOException ioe) {} } } else { - _manager.addMessage(_("Torrent was not retrieved from {0}", _url)); + _manager.addMessage(_("Torrent was not retrieved from {0}", urlify(_url))); } } finally { if (file != null) file.delete();