diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index 4fc75342f1..6143c9877f 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -160,8 +160,8 @@ public class I2PSnarkUtil { /** * fetch the given URL, returning the file it is stored in, or null on error */ - public File get(String url) { return get(url, true, 1); } - public File get(String url, boolean rewrite) { return get(url, rewrite, 1); } + public File get(String url) { return get(url, true, 0); } + public File get(String url, boolean rewrite) { return get(url, rewrite, 0); } public File get(String url, int retries) { return get(url, true, retries); } public File get(String url, boolean rewrite, int retries) { _log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy); @@ -227,12 +227,22 @@ public class I2PSnarkUtil { } } + public String lookup(String name) { + Destination dest = getDestination(name); + if (dest == null) + return null; + return dest.toBase64(); + } + /** - * Given http://blah.i2p/foo/announce turn it into http://i2p/blah/foo/announce + * Given http://KEY.i2p/foo/announce turn it into http://i2p/KEY/foo/announce + * Given http://tracker.blah.i2p/foo/announce leave it alone */ String rewriteAnnounce(String origAnnounce) { int destStart = "http://".length(); int destEnd = origAnnounce.indexOf(".i2p"); + if (destEnd < destStart + 516) + return origAnnounce; int pathStart = origAnnounce.indexOf('/', destEnd); String rv = "http://i2p/" + origAnnounce.substring(destStart, destEnd) + origAnnounce.substring(pathStart); //_log.debug("Rewriting [" + origAnnounce + "] as [" + rv + "]"); diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java index e86ad443da..f39ed698b0 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java @@ -114,6 +114,14 @@ public class Peer implements Comparable return "[unknown id] " + _id; } + /** + * Returns socket (for debug printing) + */ + public String getSocket() + { + return sock.toString(); + } + /** * The hash code of a Peer is the hash code of the peerID. */ diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 14cc0341a8..b93fa5a1e9 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -34,8 +34,12 @@ public class SnarkManager implements Snark.CompleteListener { public static final String PROP_META_PREFIX = "i2psnark.zmeta."; public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield"; - public static final String PROP_AUTO_START = "i2snark.autoStart"; + public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops public static final String DEFAULT_AUTO_START = "false"; + public static final String PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers"; + public static final String DEFAULT_USE_OPENTRACKERS = "true"; + public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers"; + public static final String DEFAULT_OPENTRACKERS = "http://tracker.welterde.i2p/a"; private SnarkManager() { _snarks = new HashMap(); @@ -72,6 +76,9 @@ public class SnarkManager implements Snark.CompleteListener { public boolean shouldAutoStart() { return Boolean.valueOf(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START+"")).booleanValue(); } + public boolean shouldUseOpenTrackers() { + return Boolean.valueOf(_config.getProperty(PROP_USE_OPENTRACKERS, DEFAULT_USE_OPENTRACKERS)).booleanValue(); + } private int getStartupDelayMinutes() { return 1; } public File getDataDir() { String dir = _config.getProperty(PROP_DIR); @@ -152,7 +159,7 @@ public class SnarkManager implements Snark.CompleteListener { public void updateConfig(String dataDir, boolean autoStart, String seedPct, String eepHost, String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts, - String upLimit) { + String upLimit, boolean useOpenTrackers, String openTrackers) { boolean changed = false; if (eepHost != null) { int port = I2PSnarkUtil.instance().getEepProxyPort(); @@ -170,7 +177,7 @@ public class SnarkManager implements Snark.CompleteListener { if (upLimit != null) { int limit = I2PSnarkUtil.instance().getMaxUploaders(); try { limit = Integer.parseInt(upLimit); } catch (NumberFormatException nfe) {} - if ( limit != I2PSnarkUtil.instance().getEepProxyPort()) { + if ( limit != I2PSnarkUtil.instance().getMaxUploaders()) { if ( limit >= Snark.MIN_TOTAL_UPLOADERS ) { I2PSnarkUtil.instance().setMaxUploaders(limit); changed = true; @@ -265,6 +272,18 @@ public class SnarkManager implements Snark.CompleteListener { addMessage("Adjusted autostart to " + autoStart); changed = true; } + if (shouldUseOpenTrackers() != useOpenTrackers) { + _config.setProperty(PROP_USE_OPENTRACKERS, useOpenTrackers + ""); + addMessage((useOpenTrackers ? "En" : "Dis") + "abled open trackers - torrent restart required to take effect"); + changed = true; + } + if (openTrackers != null) { + if (openTrackers.trim().length() > 0 && !openTrackers.trim().equals(getOpenTrackerString())) { + _config.setProperty(PROP_OPENTRACKERS, openTrackers.trim()); + addMessage("Open Tracker list changed - torrent restart required to take effect"); + changed = true; + } + } if (changed) { saveConfig(); } else { @@ -600,6 +619,7 @@ public class SnarkManager implements Snark.CompleteListener { // , "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/" // , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/" // , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php" + , "welterde", "http://BGKmlDOoH3RzFbPRfRpZV2FjpVj8~3moFftw5-dZfDf2070TOe8Tf2~DAVeaM6ZRLdmFEt~9wyFL8YMLMoLoiwGEH6IGW6rc45tstN68KsBDWZqkTohV1q9XFgK9JnCwE~Oi89xLBHsLMTHOabowWM6dkC8nI6QqJC2JODqLPIRfOVrDdkjLwtCrsckzLybNdFmgfoqF05UITDyczPsFVaHtpF1sRggOVmdvCM66otyonlzNcJbn59PA-R808vUrCPMGU~O9Wys0i-NoqtIbtWfOKnjCRFMNw5ex4n9m5Sxm9e20UkpKG6qzEuvKZWi8vTLe1NW~CBrj~vG7I3Ok4wybUFflBFOaBabxYJLlx4xTE1zJIVxlsekmAjckB4v-cQwulFeikR4LxPQ6mCQknW2HZ4JQIq6hL9AMabxjOlYnzh7kjOfRGkck8YgeozcyTvcDUcUsOuSTk06L4kdrv8h2Cozjbloi5zl6KTbj5ZTciKCxi73Pn9grICn-HQqEAAAA.i2p/a=http://tracker.welterde.i2p/stats?mode=top5" }; /** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */ @@ -634,6 +654,26 @@ public class SnarkManager implements Snark.CompleteListener { return trackerMap; } + public String getOpenTrackerString() { + return _config.getProperty(PROP_OPENTRACKERS, DEFAULT_OPENTRACKERS); + } + + /** comma delimited list open trackers to use as backups */ + /** sorted map of name to announceURL=baseURL */ + public List getOpenTrackers() { + if (!shouldUseOpenTrackers()) + return null; + List rv = new ArrayList(1); + String trackers = getOpenTrackerString(); + StringTokenizer tok = new StringTokenizer(trackers, ", "); + while (tok.hasMoreTokens()) + rv.add(tok.nextToken()); + + if (rv.size() <= 0) + return null; + return rv; + } + private static class TorrentFilenameFilter implements FilenameFilter { private static final TorrentFilenameFilter _filter = new TorrentFilenameFilter(); public static TorrentFilenameFilter instance() { return _filter; } diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index da03fc39f2..878b1831ce 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -46,6 +46,10 @@ public class TrackerClient extends I2PThread private final static int SLEEP = 5; // 5 minutes. private final static int DELAY_MIN = 2000; // 2 secs. private final static int DELAY_MUL = 1500; // 1.5 secs. + private final static int MAX_REGISTER_FAILS = 10; // * INITIAL_SLEEP = 15m to register + private final static int INITIAL_SLEEP = 90*1000; + private final static int MAX_CONSEC_FAILS = 5; // slow down after this + private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails private final MetaInfo meta; private final PeerCoordinator coordinator; @@ -54,8 +58,7 @@ public class TrackerClient extends I2PThread private boolean stop; private boolean started; - private long interval; - private long lastRequestTime; + private List trackers; public TrackerClient(MetaInfo meta, PeerCoordinator coordinator) { @@ -100,14 +103,47 @@ public class TrackerClient extends I2PThread public void run() { - // XXX - Support other IPs - String announce = meta.getAnnounce(); //I2PSnarkUtil.instance().rewriteAnnounce(meta.getAnnounce()); String infoHash = urlencode(meta.getInfoHash()); String peerID = urlencode(coordinator.getID()); - _log.debug("Announce: [" + meta.getAnnounce() + "] infoHash: " + infoHash - + " xmitAnnounce: [" + announce + "]"); + _log.debug("Announce: [" + meta.getAnnounce() + "] infoHash: " + infoHash); + // Construct the list of trackers for this torrent, + // starting with the primary one listed in the metainfo, + // followed by the secondary open trackers + // It's painful, but try to make sure if an open tracker is also + // the primary tracker, that we don't add it twice. + trackers = new ArrayList(2); + trackers.add(new Tracker(meta.getAnnounce(), true)); + List tlist = SnarkManager.instance().getOpenTrackers(); + if (tlist != null) { + for (int i = 0; i < tlist.size(); i++) { + String url = (String)tlist.get(i); + if (!url.startsWith("http://")) { + _log.error("Bad announce URL: [" + url + "]"); + continue; + } + int slash = url.indexOf('/', 7); + if (slash <= 7) { + _log.error("Bad announce URL: [" + url + "]"); + continue; + } + if (meta.getAnnounce().startsWith(url.substring(0, slash))) + continue; + String dest = I2PSnarkUtil.instance().lookup(url.substring(7, slash)); + if (dest == null) { + _log.error("Announce host unknown: [" + url + "]"); + continue; + } + if (meta.getAnnounce().startsWith("http://" + dest)) + continue; + if (meta.getAnnounce().startsWith("http://i2p/" + dest)) + continue; + trackers.add(new Tracker(url, false)); + _log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash); + } + } + long uploaded = coordinator.getUploaded(); long downloaded = coordinator.getDownloaded(); long left = coordinator.getLeft(); @@ -119,78 +155,29 @@ public class TrackerClient extends I2PThread { if (!verifyConnected()) return; boolean started = false; - while (!started) - { - sleptTime = 0; - try - { - // Send start. - TrackerInfo info = doRequest(announce, infoHash, peerID, - uploaded, downloaded, left, - STARTED_EVENT); - Set peers = info.getPeers(); - coordinator.trackerSeenPeers = peers.size(); - coordinator.trackerProblems = null; - if (!completed) { - Iterator it = peers.iterator(); - while (it.hasNext()) { - Peer cur = (Peer)it.next(); - coordinator.addPeer(cur); - int delay = DELAY_MUL; - delay *= ((int)cur.getPeerID().getAddress().calculateHash().toBase64().charAt(0)) % 10; - delay += DELAY_MIN; - sleptTime += delay; - try { Thread.sleep(delay); } catch (InterruptedException ie) {} - } - } - started = true; - } - catch (IOException ioe) - { - // Probably not fatal (if it doesn't last to long...) - Snark.debug - ("WARNING: Could not contact tracker at '" - + announce + "': " + ioe, Snark.WARNING); - coordinator.trackerProblems = ioe.getMessage(); - if (coordinator.trackerProblems.toLowerCase().startsWith(NOT_REGISTERED)) { - stop = true; - coordinator.snark.stopTorrent(); - } - } - - if (stop) - break; - - if (!started) - { - Snark.debug(" Retrying in one minute...", Snark.DEBUG); - try - { - // Sleep one minutes... - Thread.sleep(60*1000); - } - catch(InterruptedException interrupt) - { - // ignore - } - } - } - + boolean firstTime = true; + int consecutiveFails = 0; Random r = new Random(); while(!stop) { try { // Sleep some minutes... + // Sleep the minimum interval for all the trackers, but 60s minimum + // except for the first time... int delay; - if(coordinator.trackerProblems != null && !completed) { - delay = 60*1000; - } else if(completed) { - delay = 3*SLEEP*60*1000 + r.nextInt(120*1000); - } else { - delay = SLEEP*60*1000 + r.nextInt(120*1000); - delay -= sleptTime; - } + int random = r.nextInt(120*1000); + if (firstTime) { + delay = r.nextInt(30*1000); + firstTime = false; + } else if (completed && started) + delay = 3*SLEEP*60*1000 + random; + else if (coordinator.trackerProblems != null && ++consecutiveFails < MAX_CONSEC_FAILS) + delay = INITIAL_SLEEP; + else + // sleep a while, when we wake up we will contact only the trackers whose intervals have passed + delay = SLEEP*60*1000 + random; + if (delay > 0) Thread.sleep(delay); } @@ -218,21 +205,37 @@ public class TrackerClient extends I2PThread else event = NO_EVENT; + // *** loop once for each tracker // Only do a request when necessary. sleptTime = 0; - if (event == COMPLETED_EVENT - || coordinator.needPeers() - || System.currentTimeMillis() > lastRequestTime + interval) + int maxSeenPeers = 0; + for (Iterator iter = trackers.iterator(); iter.hasNext(); ) { + Tracker tr = (Tracker)iter.next(); + if ((!stop) && (!tr.stop) && + (completed || coordinator.needPeers()) && + (event == COMPLETED_EVENT || System.currentTimeMillis() > tr.lastRequestTime + tr.interval)) { try { - TrackerInfo info = doRequest(announce, infoHash, peerID, + if (!tr.started) + event = STARTED_EVENT; + TrackerInfo info = doRequest(tr, infoHash, peerID, uploaded, downloaded, left, event); coordinator.trackerProblems = null; + tr.trackerProblems = null; + tr.registerFails = 0; + tr.consecutiveFails = 0; + if (tr.isPrimary) + consecutiveFails = 0; + started = true; + tr.started = true; + Set peers = info.getPeers(); - coordinator.trackerSeenPeers = peers.size(); + tr.seenPeers = peers.size(); + if (coordinator.trackerSeenPeers < tr.seenPeers) // update rising number quickly + coordinator.trackerSeenPeers = tr.seenPeers; if ( (left > 0) && (!completed) ) { // we only want to talk to new people if we need things // from them (duh) @@ -257,16 +260,35 @@ public class TrackerClient extends I2PThread // Probably not fatal (if it doesn't last to long...) Snark.debug ("WARNING: Could not contact tracker at '" - + announce + "': " + ioe, Snark.WARNING); - coordinator.trackerProblems = ioe.getMessage(); - if (coordinator.trackerProblems.toLowerCase().startsWith(NOT_REGISTERED)) { - stop = true; - coordinator.snark.stopTorrent(); + + tr.announce + "': " + ioe, Snark.WARNING); + tr.trackerProblems = ioe.getMessage(); + // don't show secondary tracker problems to the user + if (tr.isPrimary) + coordinator.trackerProblems = tr.trackerProblems; + if (tr.trackerProblems.toLowerCase().startsWith(NOT_REGISTERED)) { + // Give a guy some time to register it if using opentrackers too + if (trackers.size() == 1) { + stop = true; + coordinator.snark.stopTorrent(); + } else { // hopefully each on the opentrackers list is really open + if (tr.registerFails++ > MAX_REGISTER_FAILS) + tr.stop = true; + } } + if (++tr.consecutiveFails == MAX_CONSEC_FAILS && tr.interval < LONG_SLEEP) + tr.interval = LONG_SLEEP; // slow down } } - } - } + if ((!tr.stop) && maxSeenPeers < tr.seenPeers) + maxSeenPeers = tr.seenPeers; + } // *** end of trackers loop here + + // we could try and total the unique peers but that's too hard for now + coordinator.trackerSeenPeers = maxSeenPeers; + if (!started) + Snark.debug(" Retrying in one minute...", Snark.DEBUG); + } // *** end of while loop + } // try catch (Throwable t) { I2PSnarkUtil.instance().debug("TrackerClient: " + t, Snark.ERROR, t); @@ -277,21 +299,27 @@ public class TrackerClient extends I2PThread { try { - if (!verifyConnected()) return; - TrackerInfo info = doRequest(announce, infoHash, peerID, uploaded, + // try to contact everybody we can + // We don't need I2CP connection for eepget + // if (!verifyConnected()) return; + for (Iterator iter = trackers.iterator(); iter.hasNext(); ) { + Tracker tr = (Tracker)iter.next(); + if (tr.started && (!tr.stop) && tr.trackerProblems == null) + doRequest(tr, infoHash, peerID, uploaded, downloaded, left, STOPPED_EVENT); + } } catch(IOException ioe) { /* ignored */ } } } - private TrackerInfo doRequest(String announce, String infoHash, + private TrackerInfo doRequest(Tracker tr, String infoHash, String peerID, long uploaded, long downloaded, long left, String event) throws IOException { - String s = announce + String s = tr.announce + "?info_hash=" + infoHash + "&peer_id=" + peerID + "&port=" + port @@ -302,6 +330,7 @@ public class TrackerClient extends I2PThread + ((event != NO_EVENT) ? ("&event=" + event) : ""); Snark.debug("Sending TrackerClient request: " + s, Snark.INFO); + tr.lastRequestTime = System.currentTimeMillis(); File fetched = I2PSnarkUtil.instance().get(s); if (fetched == null) { throw new IOException("Error fetching " + s); @@ -315,13 +344,12 @@ public class TrackerClient extends I2PThread TrackerInfo info = new TrackerInfo(in, coordinator.getID(), coordinator.getMetaInfo()); Snark.debug("TrackerClient response: " + info, Snark.INFO); - lastRequestTime = System.currentTimeMillis(); String failure = info.getFailureReason(); if (failure != null) throw new IOException(failure); - interval = info.getInterval() * 1000; + tr.interval = info.getInterval() * 1000; return info; } finally { if (in != null) try { in.close(); } catch (IOException ioe) {} @@ -347,4 +375,32 @@ public class TrackerClient extends I2PThread return sb.toString(); } + + private class Tracker + { + String announce; + boolean isPrimary; + long interval; + long lastRequestTime; + String trackerProblems; + boolean stop; + boolean started; + int registerFails; + int consecutiveFails; + int seenPeers; + + public Tracker(String a, boolean p) + { + announce = a; + isPrimary = p; + interval = INITIAL_SLEEP; + lastRequestTime = 0; + trackerProblems = null; + stop = false; + started = false; + registerFails = 0; + consecutiveFails = 0; + seenPeers = 0; + } + } } 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 0d8da42798..a381324bda 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -115,8 +115,9 @@ public class I2PSnarkServlet extends HttpServlet { out.write("\n"); for (int i = 0; i < snarks.size(); i++) { Snark snark = (Snark)snarks.get(i); - boolean showPeers = "1".equals(peerParam) || Base64.encode(snark.meta.getInfoHash()).equals(peerParam); - displaySnark(out, snark, uri, i, stats, showPeers); + boolean showDebug = "2".equals(peerParam); + boolean showPeers = showDebug || "1".equals(peerParam) || Base64.encode(snark.meta.getInfoHash()).equals(peerParam); + displaySnark(out, snark, uri, i, stats, showPeers, showDebug); } if (snarks.size() <= 0) { out.write(TABLE_EMPTY); @@ -292,7 +293,9 @@ public class I2PSnarkServlet extends HttpServlet { String i2cpPort = req.getParameter("i2cpPort"); String i2cpOpts = req.getParameter("i2cpOpts"); String upLimit = req.getParameter("upLimit"); - _manager.updateConfig(dataDir, autoStart, seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts, upLimit); + boolean useOpenTrackers = req.getParameter("useOpenTrackers") != null; + String openTrackers = req.getParameter("openTrackers"); + _manager.updateConfig(dataDir, autoStart, seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts, upLimit, useOpenTrackers, openTrackers); } else if ("Create torrent".equals(action)) { String baseData = req.getParameter("baseFile"); if (baseData != null) { @@ -367,7 +370,7 @@ public class I2PSnarkServlet extends HttpServlet { private static final int MAX_DISPLAYED_FILENAME_LENGTH = 60; private static final int MAX_DISPLAYED_ERROR_LENGTH = 40; - private void displaySnark(PrintWriter out, Snark snark, String uri, int row, long stats[], boolean showPeers) throws IOException { + private void displaySnark(PrintWriter out, Snark snark, String uri, int row, long stats[], boolean showPeers, boolean showDebug) throws IOException { String filename = snark.torrent; File f = new File(filename); filename = f.getName(); // the torrent may be the canonical name, so lets just grab the local name @@ -551,6 +554,8 @@ public class I2PSnarkServlet extends HttpServlet { else client = "Unknown"; out.write("" + client + " " + peer.toString().substring(5, 9) + ""); + if (showDebug) + out.write(" inactive " + (peer.getInactiveTime() / 1000) + "s"); out.write("\n\t"); out.write("