diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index 98bcd4eed4..1908a11937 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -254,6 +254,7 @@ public class Snark private boolean stopped; private byte[] id; private byte[] infoHash; + private String additionalTrackerURL; private final I2PSnarkUtil _util; private final PeerCoordinatorSet _peerCoordinatorSet; private String trackerProblems; @@ -453,9 +454,10 @@ public class Snark * * @param torrent a fake name for now (not a file name) * @param ih 20-byte info hash + * @param trackerURL may be null * @since 0.8.4 */ - public Snark(I2PSnarkUtil util, String torrent, byte[] ih, + public Snark(I2PSnarkUtil util, String torrent, byte[] ih, String trackerURL, CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet, ConnectionAcceptor connectionAcceptor, boolean start, String rootDir) { @@ -465,6 +467,7 @@ public class Snark acceptor = connectionAcceptor; this.torrent = torrent; this.infoHash = ih; + this.additionalTrackerURL = trackerURL; this.rootDataDir = rootDir; stopped = true; id = generateID(); @@ -535,7 +538,7 @@ public class Snark acceptor = new ConnectionAcceptor(_util, serversocket, new PeerAcceptor(coordinator)); } // TODO pass saved closest DHT nodes to the tracker? or direct to the coordinator? - trackerclient = new TrackerClient(_util, meta, coordinator, this); + trackerclient = new TrackerClient(_util, meta, additionalTrackerURL, coordinator, this); } stopped = false; @@ -564,11 +567,13 @@ public class Snark fatal("Could not reopen storage", ioe); } } - TrackerClient newClient = new TrackerClient(_util, meta, coordinator, this); + TrackerClient newClient = new TrackerClient(_util, meta, additionalTrackerURL, coordinator, this); if (!trackerclient.halted()) trackerclient.halt(); trackerclient = newClient; trackerclient.start(); + } else { + debug("NOT starting TrackerClient???", NOTICE); } } /** @@ -825,6 +830,14 @@ public class Snark return true; } + /** + * @return trackerURL string from magnet-mode constructor, may be null + * @since 0.8.4 + */ + public String getTrackerURL() { + return additionalTrackerURL; + } + /** * Sets debug, ip and torrent variables then creates a Snark * instance. Calls usage(), which terminates the program, if diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index f0f7a1f543..29c6a1ed2b 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -643,11 +643,15 @@ public class SnarkManager implements Snark.CompleteListener { * * @param name hex or b32 name from the magnet link * @param ih 20 byte info hash + * @param trackerURL may be null + * @param udpateStatus should we add this magnet to the config file, + * to save it across restarts, in case we don't get + * the metadata before shutdown? * @throws RuntimeException via Snark.fatal() * @since 0.8.4 */ - public void addMagnet(String name, byte[] ih, boolean updateStatus) { - Snark torrent = new Snark(_util, name, ih, this, + public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus) { + Snark torrent = new Snark(_util, name, ih, trackerURL, this, _peerCoordinatorSet, _connectionAcceptor, false, getDataDir().getPath()); @@ -1151,6 +1155,10 @@ public class SnarkManager implements Snark.CompleteListener { saveTorrentStatus(meta, storage.getBitField(), null); // no file priorities String name = (new File(getDataDir(), storage.getBaseName() + ".torrent")).getAbsolutePath(); try { + // put the announce URL in the file + String announce = snark.getTrackerURL(); + if (announce != null) + meta = meta.reannounce(announce); synchronized (_snarks) { locked_writeMetaInfo(meta, name); // put it in the list under the new name @@ -1183,9 +1191,9 @@ public class SnarkManager implements Snark.CompleteListener { String b64 = k.substring(PROP_META_MAGNET_PREFIX.length()); b64 = b64.replace('$', '='); byte[] ih = Base64.decode(b64); - // ignore value + // ignore value - TODO put tracker URL in value if (ih != null && ih.length == 20) - addMagnet("Magnet: " + I2PSnarkUtil.toHex(ih), ih, false); + addMagnet("Magnet: " + I2PSnarkUtil.toHex(ih), ih, null, false); // else remove from config? } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index c8f1cd6c96..ad4cdae395 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -65,6 +65,7 @@ public class TrackerClient extends I2PAppThread private I2PSnarkUtil _util; private final MetaInfo meta; + private final String additionalTrackerURL; private final PeerCoordinator coordinator; private final Snark snark; private final int port; @@ -76,8 +77,10 @@ public class TrackerClient extends I2PAppThread /** * @param meta null if in magnet mode + * @param additionalTrackerURL may be null, from the ?tr= param in magnet mode, otherwise ignored */ - public TrackerClient(I2PSnarkUtil util, MetaInfo meta, PeerCoordinator coordinator, Snark snark) + public TrackerClient(I2PSnarkUtil util, MetaInfo meta, String additionalTrackerURL, + PeerCoordinator coordinator, Snark snark) { super(); // Set unique name. @@ -85,13 +88,11 @@ public class TrackerClient extends I2PAppThread setName("TrackerClient " + id.substring(id.length() - 12)); _util = util; this.meta = meta; + this.additionalTrackerURL = additionalTrackerURL; this.coordinator = coordinator; this.snark = snark; this.port = 6881; //(port == -1) ? 9 : port; - - stop = false; - started = false; } @Override @@ -138,17 +139,21 @@ public class TrackerClient extends I2PAppThread // todo: check for b32 matches as well trackers = new ArrayList(2); String primary = null; - if (meta != null) { + if (meta != null) primary = meta.getAnnounce(); + else if (additionalTrackerURL != null) + primary = additionalTrackerURL; + if (primary != null) { if (isValidAnnounce(primary)) { - trackers.add(new Tracker(meta.getAnnounce(), true)); + trackers.add(new Tracker(primary, true)); _log.debug("Announce: [" + primary + "] infoHash: " + infoHash); } else { _log.warn("Skipping invalid or non-i2p announce: " + primary); } - } - if (primary == null) + } else { + _log.warn("No primary announce"); primary = ""; + } List tlist = _util.getOpenTrackers(); if (tlist != null) { for (int i = 0; i < tlist.size(); i++) { 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 0b15de790f..a3d77a814a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -65,7 +65,8 @@ public class I2PSnarkServlet extends Default { public static final String PROP_CONFIG_FILE = "i2psnark.configFile"; /** BEP 9 */ - private static final String MAGNET = "magnet:?xt=urn:btih:"; + private static final String MAGNET = "magnet:"; + private static final String MAGNET_FULL = MAGNET + "?xt=urn:btih:"; /** http://sponge.i2p/files/maggotspec.txt */ private static final String MAGGOT = "maggot://"; @@ -1406,13 +1407,22 @@ public class I2PSnarkServlet extends Default { private void addMagnet(String url) { String ihash; String name; + String trackerURL = null; if (url.startsWith(MAGNET)) { - ihash = url.substring(MAGNET.length()).trim(); - int amp = ihash.indexOf('&'); - if (amp >= 0) - ihash = ihash.substring(0, amp); + // magnet:?xt=urn:btih:0691e40aae02e552cfcb57af1dca56214680c0c5&tr=http://tracker2.postman.i2p/announce.php + String xt = getParam("xt", url); + if (xt == null || !xt.startsWith("urn:btih:")) { + _manager.addMessage(_("Invalid magnet URL {0}", url)); + return; + } + ihash = xt.substring("urn:btih:".length()); + trackerURL = getParam("tr", url); name = "Magnet " + ihash; + String dn = getParam("dn", url); + if (dn != null) + name += " (" + Storage.filterName(dn) + ')'; } else if (url.startsWith(MAGGOT)) { + // maggot://0691e40aae02e552cfcb57af1dca56214680c0c5:0b557bbdf8718e95d352fbe994dec3a383e2ede7 ihash = url.substring(MAGGOT.length()).trim(); int col = ihash.indexOf(':'); if (col >= 0) @@ -1439,7 +1449,27 @@ public class I2PSnarkServlet extends Default { _manager.addMessage(_("Invalid info hash in magnet URL {0}", url)); return; } - _manager.addMagnet(name, ih, true); + _manager.addMagnet(name, ih, trackerURL, true); + } + + private static String getParam(String key, String uri) { + int idx = uri.indexOf('?' + key + '='); + if (idx >= 0) { + idx += key.length() + 2; + } else { + idx = uri.indexOf('&' + key + '='); + if (idx >= 0) + idx += key.length() + 2; + } + if (idx < 0 || idx > uri.length()) + return null; + String rv = uri.substring(idx); + idx = rv.indexOf('&'); + if (idx >= 0) + rv = rv.substring(0, idx); + else + rv = rv.trim(); + return rv; } /** copied from ConfigTunnelsHelper */ @@ -1644,8 +1674,8 @@ public class I2PSnarkServlet extends Default { String hex = I2PSnarkUtil.toHex(snark.getInfoHash()); buf.append("
").append(toImg("magnet", _("Magnet link"))).append(" ") - .append(MAGNET).append(hex).append(""); + .append(MAGNET_FULL).append(hex).append("\">") + .append(MAGNET_FULL).append(hex).append(""); // We don't have the hash of the torrent file //buf.append("
").append(_("Maggot link")).append(": ") // .append(MAGGOT).append(hex).append(':').append(hex).append(""); diff --git a/history.txt b/history.txt index ce975c6879..3cee76c487 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,12 @@ +2011-02-10 zzz + * I2CP: Correctly close internal connections on the router side + when closed by the client, was causing massive memory leak + for internal clients using lots of sessions (thanks sponge) + * i2psnark: + - Improved magnet link parsing, use tr parameter if present + * i2ptunnel: Change shared clients default for new clients to false + * Streaming: Don't use iter.remove() on a COWAS + 2011-02-09 sponge * BOB: fixup delivery in config, adds config file versioning. * I2CP: Fix most of the I2CP leaks. Two leaks remain, but they are small. diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 4e28a2c8d2..c10128fe62 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 5; + public final static long BUILD = 6; /** for example "-test" */ public final static String EXTRA = "";