forked from I2P_Developers/i2p.i2p
- Hide I2CP settings when in router context
- Better BEValue.toString() (most of the following got missed in the last checkin) - Fix about 9 NPEs - Fix numwant in magnet mode - Send metadata size in extension handshake - Open trackers are primary if we don't have primary trackers - Add missing break in port message handling - Increase max msg size to account for metadata msg - Remember magnets across restarts - Drop peers w/o extensions if we need metainfo - Fix DATA messages - Fix tracker transition to non-magnet - Fix infohash for non-magnet - Fix up peer transition to non-magnet - More logging
This commit is contained in:
@ -21,7 +21,6 @@ import org.klomp.snark.bencode.InvalidBEncodingException;
|
|||||||
*/
|
*/
|
||||||
abstract class ExtensionHandler {
|
abstract class ExtensionHandler {
|
||||||
|
|
||||||
private static final byte[] _handshake = buildHandshake();
|
|
||||||
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ExtensionHandler.class);
|
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ExtensionHandler.class);
|
||||||
|
|
||||||
public static final int ID_METADATA = 3;
|
public static final int ID_METADATA = 3;
|
||||||
@ -32,17 +31,15 @@ abstract class ExtensionHandler {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param metasize -1 if unknown
|
||||||
* @return bencoded outgoing handshake message
|
* @return bencoded outgoing handshake message
|
||||||
*/
|
*/
|
||||||
public static byte[] getHandshake() {
|
public static byte[] getHandshake(int metasize) {
|
||||||
return _handshake;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** outgoing handshake message */
|
|
||||||
private static byte[] buildHandshake() {
|
|
||||||
Map<String, Object> handshake = new HashMap();
|
Map<String, Object> handshake = new HashMap();
|
||||||
Map<String, Integer> m = new HashMap();
|
Map<String, Integer> m = new HashMap();
|
||||||
m.put(TYPE_METADATA, Integer.valueOf(ID_METADATA));
|
m.put(TYPE_METADATA, Integer.valueOf(ID_METADATA));
|
||||||
|
if (metasize >= 0)
|
||||||
|
handshake.put("metadata_size", Integer.valueOf(metasize));
|
||||||
handshake.put("m", m);
|
handshake.put("m", m);
|
||||||
handshake.put("p", Integer.valueOf(6881));
|
handshake.put("p", Integer.valueOf(6881));
|
||||||
handshake.put("v", "I2PSnark");
|
handshake.put("v", "I2PSnark");
|
||||||
@ -51,6 +48,8 @@ abstract class ExtensionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void handleMessage(Peer peer, PeerListener listener, int id, byte[] bs) {
|
public static void handleMessage(Peer peer, PeerListener listener, int id, byte[] bs) {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Got extension msg " + id + " length " + bs.length + " from " + peer);
|
||||||
if (id == 0)
|
if (id == 0)
|
||||||
handleHandshake(peer, listener, bs);
|
handleHandshake(peer, listener, bs);
|
||||||
else if (id == ID_METADATA)
|
else if (id == ID_METADATA)
|
||||||
@ -71,10 +70,24 @@ abstract class ExtensionHandler {
|
|||||||
peer.setHandshakeMap(map);
|
peer.setHandshakeMap(map);
|
||||||
Map<String, BEValue> msgmap = map.get("m").getMap();
|
Map<String, BEValue> msgmap = map.get("m").getMap();
|
||||||
|
|
||||||
// rv not used, just to throw an NPE to get out of here
|
if (msgmap.get(TYPE_METADATA) == null) {
|
||||||
msgmap.get(TYPE_METADATA).getInt();
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.debug("Peer does not support metadata extension: " + peer);
|
||||||
|
// drop if we need metainfo ?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BEValue msize = map.get("metadata_size");
|
||||||
|
if (msize == null) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.debug("Peer does not have the metainfo size yet: " + peer);
|
||||||
|
// drop if we need metainfo ?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int metaSize = msize.getInt();
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.debug("Got the metainfo size: " + metaSize);
|
||||||
|
|
||||||
int metaSize = map.get("metadata_size").getInt();
|
|
||||||
MagnetState state = peer.getMagnetState();
|
MagnetState state = peer.getMagnetState();
|
||||||
int remaining;
|
int remaining;
|
||||||
synchronized(state) {
|
synchronized(state) {
|
||||||
@ -83,12 +96,16 @@ abstract class ExtensionHandler {
|
|||||||
|
|
||||||
if (state.isInitialized()) {
|
if (state.isInitialized()) {
|
||||||
if (state.getSize() != metaSize) {
|
if (state.getSize() != metaSize) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.debug("Wrong metainfo size " + metaSize + " from: " + peer);
|
||||||
peer.disconnect();
|
peer.disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// initialize it
|
// initialize it
|
||||||
if (metaSize > MAX_METADATA_SIZE) {
|
if (metaSize > MAX_METADATA_SIZE) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.debug("Huge metainfo size " + metaSize + " from: " + peer);
|
||||||
peer.disconnect(false);
|
peer.disconnect(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -112,8 +129,7 @@ abstract class ExtensionHandler {
|
|||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.info("Handshake exception from " + peer, e);
|
_log.warn("Handshake exception from " + peer, e);
|
||||||
//peer.disconnect(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +156,8 @@ abstract class ExtensionHandler {
|
|||||||
|
|
||||||
MagnetState state = peer.getMagnetState();
|
MagnetState state = peer.getMagnetState();
|
||||||
if (type == TYPE_REQUEST) {
|
if (type == TYPE_REQUEST) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Got request for " + piece + " from: " + peer);
|
||||||
byte[] pc;
|
byte[] pc;
|
||||||
synchronized(state) {
|
synchronized(state) {
|
||||||
pc = state.getChunk(piece);
|
pc = state.getChunk(piece);
|
||||||
@ -150,12 +168,19 @@ abstract class ExtensionHandler {
|
|||||||
listener.uploaded(peer, pc.length);
|
listener.uploaded(peer, pc.length);
|
||||||
} else if (type == TYPE_DATA) {
|
} else if (type == TYPE_DATA) {
|
||||||
int size = map.get("total_size").getInt();
|
int size = map.get("total_size").getInt();
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Got data for " + piece + " length " + size + " from: " + peer);
|
||||||
boolean done;
|
boolean done;
|
||||||
int chk = -1;
|
int chk = -1;
|
||||||
synchronized(state) {
|
synchronized(state) {
|
||||||
if (state.isComplete())
|
if (state.isComplete())
|
||||||
return;
|
return;
|
||||||
int len = is.available();
|
int len = is.available();
|
||||||
|
if (len != size) {
|
||||||
|
// probably fatal
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("total_size " + size + " but avail data " + len);
|
||||||
|
}
|
||||||
peer.downloaded(len);
|
peer.downloaded(len);
|
||||||
listener.downloaded(peer, len);
|
listener.downloaded(peer, len);
|
||||||
done = state.saveChunk(piece, bs, bs.length - len, len);
|
done = state.saveChunk(piece, bs, bs.length - len, len);
|
||||||
@ -219,15 +244,15 @@ abstract class ExtensionHandler {
|
|||||||
|
|
||||||
private static void sendPiece(Peer peer, int piece, byte[] data) {
|
private static void sendPiece(Peer peer, int piece, byte[] data) {
|
||||||
Map<String, Object> map = new HashMap();
|
Map<String, Object> map = new HashMap();
|
||||||
map.put("msg_type", Integer.valueOf(TYPE_REQUEST));
|
map.put("msg_type", Integer.valueOf(TYPE_DATA));
|
||||||
map.put("piece", Integer.valueOf(piece));
|
map.put("piece", Integer.valueOf(piece));
|
||||||
map.put("total_size", Integer.valueOf(data.length));
|
map.put("total_size", Integer.valueOf(data.length));
|
||||||
byte[] dict = BEncoder.bencode(map);
|
byte[] dict = BEncoder.bencode(map);
|
||||||
byte[] payload = new byte[dict.length + data.length];
|
byte[] payload = new byte[dict.length + data.length];
|
||||||
System.arraycopy(dict, 0, payload, 0, dict.length);
|
System.arraycopy(dict, 0, payload, 0, dict.length);
|
||||||
System.arraycopy(data, 0, payload, dict.length, payload.length);
|
System.arraycopy(data, 0, payload, dict.length, data.length);
|
||||||
try {
|
try {
|
||||||
int hisMsgCode = peer.getHandshakeMap().get("m").getMap().get("METADATA").getInt();
|
int hisMsgCode = peer.getHandshakeMap().get("m").getMap().get(TYPE_METADATA).getInt();
|
||||||
peer.sendExtension(hisMsgCode, payload);
|
peer.sendExtension(hisMsgCode, payload);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// NPE, no metadata caps
|
// NPE, no metadata caps
|
||||||
|
@ -446,6 +446,21 @@ public class I2PSnarkUtil {
|
|||||||
return Boolean.valueOf(rv).booleanValue();
|
return Boolean.valueOf(rv).booleanValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like DataHelper.toHexString but ensures no loss of leading zero bytes
|
||||||
|
* @since 0.8.4
|
||||||
|
*/
|
||||||
|
public static String toHex(byte[] b) {
|
||||||
|
StringBuilder buf = new StringBuilder(40);
|
||||||
|
for (int i = 0; i < b.length; i++) {
|
||||||
|
int bi = b[i] & 0xff;
|
||||||
|
if (bi < 16)
|
||||||
|
buf.append('0');
|
||||||
|
buf.append(Integer.toHexString(bi));
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/** hook between snark's logger and an i2p log */
|
/** hook between snark's logger and an i2p log */
|
||||||
void debug(String msg, int snarkDebugLevel) {
|
void debug(String msg, int snarkDebugLevel) {
|
||||||
debug(msg, snarkDebugLevel, null);
|
debug(msg, snarkDebugLevel, null);
|
||||||
|
@ -53,8 +53,6 @@ class MagnetState {
|
|||||||
metainfo = meta;
|
metainfo = meta;
|
||||||
initialize(meta.getInfoBytes().length);
|
initialize(meta.getInfoBytes().length);
|
||||||
complete = true;
|
complete = true;
|
||||||
} else {
|
|
||||||
metainfoBytes = new byte[metaSize];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,10 +66,13 @@ class MagnetState {
|
|||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
metaSize = size;
|
metaSize = size;
|
||||||
totalChunks = (size + (CHUNK_SIZE - 1)) / CHUNK_SIZE;
|
totalChunks = (size + (CHUNK_SIZE - 1)) / CHUNK_SIZE;
|
||||||
if (metainfo == null) {
|
if (metainfo != null) {
|
||||||
|
metainfoBytes = metainfo.getInfoBytes();
|
||||||
|
} else {
|
||||||
// we don't need these if complete
|
// we don't need these if complete
|
||||||
have = new BitField(totalChunks);
|
have = new BitField(totalChunks);
|
||||||
requested = new BitField(totalChunks);
|
requested = new BitField(totalChunks);
|
||||||
|
metainfoBytes = new byte[metaSize];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,8 +195,7 @@ class MagnetState {
|
|||||||
InputStream is = new ByteArrayInputStream(metainfoBytes);
|
InputStream is = new ByteArrayInputStream(metainfoBytes);
|
||||||
BDecoder dec = new BDecoder(is);
|
BDecoder dec = new BDecoder(is);
|
||||||
BEValue bev = dec.bdecodeMap();
|
BEValue bev = dec.bdecodeMap();
|
||||||
Map<String, BEValue> info = bev.getMap();
|
map.put("info", bev);
|
||||||
map.put("info", info);
|
|
||||||
MetaInfo newmeta = new MetaInfo(map);
|
MetaInfo newmeta = new MetaInfo(map);
|
||||||
if (!DataHelper.eq(newmeta.getInfoHash(), infohash))
|
if (!DataHelper.eq(newmeta.getInfoHash(), infohash))
|
||||||
throw new IOException("info hash mismatch");
|
throw new IOException("info hash mismatch");
|
||||||
|
@ -104,7 +104,7 @@ class Message
|
|||||||
if (type == REQUEST || type == CANCEL)
|
if (type == REQUEST || type == CANCEL)
|
||||||
datalen += 4;
|
datalen += 4;
|
||||||
|
|
||||||
// length is 1 byte
|
// msg type is 1 byte
|
||||||
if (type == EXTENSION)
|
if (type == EXTENSION)
|
||||||
datalen += 1;
|
datalen += 1;
|
||||||
|
|
||||||
@ -167,6 +167,8 @@ class Message
|
|||||||
return "PIECE(" + piece + "," + begin + "," + length + ")";
|
return "PIECE(" + piece + "," + begin + "," + length + ")";
|
||||||
case CANCEL:
|
case CANCEL:
|
||||||
return "CANCEL(" + piece + "," + begin + "," + length + ")";
|
return "CANCEL(" + piece + "," + begin + "," + length + ")";
|
||||||
|
case PORT:
|
||||||
|
return "PORT(" + piece + ")";
|
||||||
case EXTENSION:
|
case EXTENSION:
|
||||||
return "EXTENSION(" + piece + ',' + data.length + ')';
|
return "EXTENSION(" + piece + ',' + data.length + ')';
|
||||||
default:
|
default:
|
||||||
|
@ -120,7 +120,7 @@ public class Peer implements Comparable
|
|||||||
this.peerID = new PeerID(id, sock.getPeerDestination());
|
this.peerID = new PeerID(id, sock.getPeerDestination());
|
||||||
_id = ++__id;
|
_id = ++__id;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Creating a new peer with " + peerID.toString(), new Exception("creating " + _id));
|
_log.debug("Creating a new peer " + peerID.toString(), new Exception("creating " + _id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -261,14 +261,22 @@ public class Peer implements Comparable
|
|||||||
_log.debug("Already have din [" + sock + "] with " + toString());
|
_log.debug("Already have din [" + sock + "] with " + toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bad idea?
|
||||||
|
if (metainfo == null && (options & OPTION_EXTENSION) == 0) {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Peer does not support extensions and we need metainfo, dropping");
|
||||||
|
throw new IOException("Peer does not support extensions and we need metainfo, dropping");
|
||||||
|
}
|
||||||
|
|
||||||
PeerConnectionIn in = new PeerConnectionIn(this, din);
|
PeerConnectionIn in = new PeerConnectionIn(this, din);
|
||||||
PeerConnectionOut out = new PeerConnectionOut(this, dout);
|
PeerConnectionOut out = new PeerConnectionOut(this, dout);
|
||||||
PeerState s = new PeerState(this, listener, metainfo, in, out);
|
PeerState s = new PeerState(this, listener, metainfo, in, out);
|
||||||
|
|
||||||
if ((options & OPTION_EXTENSION) != 0) {
|
if ((options & OPTION_EXTENSION) != 0) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Peer supports extensions, sending test message");
|
_log.debug("Peer supports extensions, sending reply message");
|
||||||
out.sendExtension(0, ExtensionHandler.getHandshake());
|
int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1;
|
||||||
|
out.sendExtension(0, ExtensionHandler.getHandshake(metasize));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((options & OPTION_DHT) != 0 && util.getDHT() != null) {
|
if ((options & OPTION_DHT) != 0 && util.getDHT() != null) {
|
||||||
@ -423,6 +431,7 @@ public class Peer implements Comparable
|
|||||||
* @since 0.8.4
|
* @since 0.8.4
|
||||||
*/
|
*/
|
||||||
public void setMetaInfo(MetaInfo meta) {
|
public void setMetaInfo(MetaInfo meta) {
|
||||||
|
metainfo = meta;
|
||||||
PeerState s = state;
|
PeerState s = state;
|
||||||
if (s != null)
|
if (s != null)
|
||||||
s.setMetaInfo(meta);
|
s.setMetaInfo(meta);
|
||||||
|
@ -32,6 +32,13 @@ class PeerConnectionIn implements Runnable
|
|||||||
private final Peer peer;
|
private final Peer peer;
|
||||||
private final DataInputStream din;
|
private final DataInputStream din;
|
||||||
|
|
||||||
|
// The max length of a complete message in bytes.
|
||||||
|
// The biggest is the piece message, for which the length is the
|
||||||
|
// request size (32K) plus 9. (we could also check if Storage.MAX_PIECES / 8
|
||||||
|
// in the bitfield message is bigger but it's currently 5000/8 = 625 so don't bother)
|
||||||
|
private static final int MAX_MSG_SIZE = Math.max(PeerState.PARTSIZE + 9,
|
||||||
|
MagnetState.CHUNK_SIZE + 100); // 100 for the ext msg dictionary
|
||||||
|
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private volatile boolean quit;
|
private volatile boolean quit;
|
||||||
|
|
||||||
@ -77,13 +84,9 @@ class PeerConnectionIn implements Runnable
|
|||||||
int len;
|
int len;
|
||||||
|
|
||||||
// Wait till we hear something...
|
// Wait till we hear something...
|
||||||
// The length of a complete message in bytes.
|
|
||||||
// The biggest is the piece message, for which the length is the
|
|
||||||
// request size (32K) plus 9. (we could also check if Storage.MAX_PIECES / 8
|
|
||||||
// in the bitfield message is bigger but it's currently 5000/8 = 625 so don't bother)
|
|
||||||
int i = din.readInt();
|
int i = din.readInt();
|
||||||
lastRcvd = System.currentTimeMillis();
|
lastRcvd = System.currentTimeMillis();
|
||||||
if (i < 0 || i > PeerState.PARTSIZE + 9)
|
if (i < 0 || i > MAX_MSG_SIZE)
|
||||||
throw new IOException("Unexpected length prefix: " + i);
|
throw new IOException("Unexpected length prefix: " + i);
|
||||||
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
@ -176,13 +179,14 @@ class PeerConnectionIn implements Runnable
|
|||||||
ps.portMessage(port);
|
ps.portMessage(port);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Received port message from " + peer);
|
_log.debug("Received port message from " + peer);
|
||||||
|
break;
|
||||||
case 20: // Extension message
|
case 20: // Extension message
|
||||||
int id = din.readUnsignedByte();
|
int id = din.readUnsignedByte();
|
||||||
byte[] payload = new byte[i-2];
|
byte[] payload = new byte[i-2];
|
||||||
din.readFully(payload);
|
din.readFully(payload);
|
||||||
ps.extensionMessage(id, payload);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Received extension message from " + peer);
|
_log.debug("Received extension message from " + peer);
|
||||||
|
ps.extensionMessage(id, payload);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
byte[] bs = new byte[i-1];
|
byte[] bs = new byte[i-1];
|
||||||
|
@ -410,7 +410,7 @@ public class PeerCoordinator implements PeerListener
|
|||||||
name = "Magnet";
|
name = "Magnet";
|
||||||
else
|
else
|
||||||
name = metainfo.getName();
|
name = metainfo.getName();
|
||||||
_log.info("New connection to peer: " + peer + " for " + metainfo.getName());
|
_log.info("New connection to peer: " + peer + " for " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add it to the beginning of the list.
|
// Add it to the beginning of the list.
|
||||||
@ -1169,9 +1169,6 @@ public class PeerCoordinator implements PeerListener
|
|||||||
_log.warn("Got completed metainfo via extension");
|
_log.warn("Got completed metainfo via extension");
|
||||||
metainfo = magnetState.getMetaInfo();
|
metainfo = magnetState.getMetaInfo();
|
||||||
listener.gotMetaInfo(this, metainfo);
|
listener.gotMetaInfo(this, metainfo);
|
||||||
for (Peer p : peers) {
|
|
||||||
p.setMetaInfo(metainfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1184,6 +1181,11 @@ public class PeerCoordinator implements PeerListener
|
|||||||
*/
|
*/
|
||||||
public void setStorage(Storage stg) {
|
public void setStorage(Storage stg) {
|
||||||
storage = stg;
|
storage = stg;
|
||||||
|
setWantedPieces();
|
||||||
|
// ok we should be in business
|
||||||
|
for (Peer p : peers) {
|
||||||
|
p.setMetaInfo(metainfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -511,6 +511,8 @@ class PeerState implements DataLoader
|
|||||||
//bitfield = new BitField(meta.getPieces());
|
//bitfield = new BitField(meta.getPieces());
|
||||||
}
|
}
|
||||||
metainfo = meta;
|
metainfo = meta;
|
||||||
|
if (bitfield.count() > 0)
|
||||||
|
setInteresting(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 0.8.4 */
|
/** @since 0.8.4 */
|
||||||
|
@ -243,7 +243,7 @@ public class Snark
|
|||||||
public static final String PROP_MAX_CONNECTIONS = "i2psnark.maxConnections";
|
public static final String PROP_MAX_CONNECTIONS = "i2psnark.maxConnections";
|
||||||
|
|
||||||
/** most of these used to be public, use accessors below instead */
|
/** most of these used to be public, use accessors below instead */
|
||||||
private final String torrent;
|
private String torrent;
|
||||||
private MetaInfo meta;
|
private MetaInfo meta;
|
||||||
private Storage storage;
|
private Storage storage;
|
||||||
private PeerCoordinator coordinator;
|
private PeerCoordinator coordinator;
|
||||||
@ -360,6 +360,7 @@ public class Snark
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
meta = new MetaInfo(new BDecoder(in));
|
meta = new MetaInfo(new BDecoder(in));
|
||||||
|
infoHash = meta.getInfoHash();
|
||||||
}
|
}
|
||||||
catch(IOException ioe)
|
catch(IOException ioe)
|
||||||
{
|
{
|
||||||
@ -1028,8 +1029,13 @@ public class Snark
|
|||||||
meta = metainfo;
|
meta = metainfo;
|
||||||
try {
|
try {
|
||||||
storage = new Storage(_util, meta, this);
|
storage = new Storage(_util, meta, this);
|
||||||
if (completeListener != null)
|
storage.check(rootDataDir);
|
||||||
completeListener.gotMetaInfo(this);
|
if (completeListener != null) {
|
||||||
|
String newName = completeListener.gotMetaInfo(this);
|
||||||
|
if (newName != null)
|
||||||
|
torrent = newName;
|
||||||
|
// else some horrible problem
|
||||||
|
}
|
||||||
coordinator.setStorage(storage);
|
coordinator.setStorage(storage);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
if (storage != null) {
|
if (storage != null) {
|
||||||
@ -1125,9 +1131,10 @@ public class Snark
|
|||||||
* metainfo and storage. The listener should now call getMetaInfo()
|
* metainfo and storage. The listener should now call getMetaInfo()
|
||||||
* and save the data to disk.
|
* and save the data to disk.
|
||||||
*
|
*
|
||||||
|
* @return the new name for the torrent or null on error
|
||||||
* @since 0.8.4
|
* @since 0.8.4
|
||||||
*/
|
*/
|
||||||
public void gotMetaInfo(Snark snark);
|
public String gotMetaInfo(Snark snark);
|
||||||
|
|
||||||
// not really listeners but the easiest way to get back to an optional SnarkManager
|
// not really listeners but the easiest way to get back to an optional SnarkManager
|
||||||
public long getSavedTorrentTime(Snark snark);
|
public long getSavedTorrentTime(Snark snark);
|
||||||
|
@ -68,7 +68,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
|
public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
|
||||||
public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
||||||
public static final String PROP_META_PRIORITY_SUFFIX = ".priority";
|
public static final String PROP_META_PRIORITY_SUFFIX = ".priority";
|
||||||
public static final String PROP_META_MAGNET_SUFFIX = ".magnet";
|
public static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
|
||||||
|
|
||||||
private static final String CONFIG_FILE = "i2psnark.config";
|
private static final String CONFIG_FILE = "i2psnark.config";
|
||||||
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
|
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
|
||||||
@ -340,7 +340,9 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
int oldI2CPPort = _util.getI2CPPort();
|
int oldI2CPPort = _util.getI2CPPort();
|
||||||
String oldI2CPHost = _util.getI2CPHost();
|
String oldI2CPHost = _util.getI2CPHost();
|
||||||
int port = oldI2CPPort;
|
int port = oldI2CPPort;
|
||||||
|
if (i2cpPort != null) {
|
||||||
try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {}
|
try { port = Integer.parseInt(i2cpPort); } catch (NumberFormatException nfe) {}
|
||||||
|
}
|
||||||
String host = oldI2CPHost;
|
String host = oldI2CPHost;
|
||||||
Map opts = new HashMap();
|
Map opts = new HashMap();
|
||||||
if (i2cpOpts == null) i2cpOpts = "";
|
if (i2cpOpts == null) i2cpOpts = "";
|
||||||
@ -627,7 +629,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
* @throws RuntimeException via Snark.fatal()
|
* @throws RuntimeException via Snark.fatal()
|
||||||
* @since 0.8.4
|
* @since 0.8.4
|
||||||
*/
|
*/
|
||||||
public void addMagnet(String name, byte[] ih) {
|
public void addMagnet(String name, byte[] ih, boolean updateStatus) {
|
||||||
Snark torrent = new Snark(_util, name, ih, this,
|
Snark torrent = new Snark(_util, name, ih, this,
|
||||||
_peerCoordinatorSet, _connectionAcceptor,
|
_peerCoordinatorSet, _connectionAcceptor,
|
||||||
false, getDataDir().getPath());
|
false, getDataDir().getPath());
|
||||||
@ -640,6 +642,8 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
}
|
}
|
||||||
// Tell the dir monitor not to delete us
|
// Tell the dir monitor not to delete us
|
||||||
_magnets.add(name);
|
_magnets.add(name);
|
||||||
|
if (updateStatus)
|
||||||
|
saveMagnetStatus(ih);
|
||||||
_snarks.put(name, torrent);
|
_snarks.put(name, torrent);
|
||||||
}
|
}
|
||||||
if (shouldAutoStart()) {
|
if (shouldAutoStart()) {
|
||||||
@ -667,6 +671,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
}
|
}
|
||||||
snark.stopTorrent();
|
snark.stopTorrent();
|
||||||
_magnets.remove(snark.getName());
|
_magnets.remove(snark.getName());
|
||||||
|
removeMagnetStatus(snark.getInfoHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -914,6 +919,28 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just remember we have it
|
||||||
|
* @since 0.8.4
|
||||||
|
*/
|
||||||
|
public void saveMagnetStatus(byte[] ih) {
|
||||||
|
String infohash = Base64.encode(ih);
|
||||||
|
infohash = infohash.replace('=', '$');
|
||||||
|
_config.setProperty(PROP_META_MAGNET_PREFIX + infohash, ".");
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the magnet marker from the config file.
|
||||||
|
* @since 0.8.4
|
||||||
|
*/
|
||||||
|
public void removeMagnetStatus(byte[] ih) {
|
||||||
|
String infohash = Base64.encode(ih);
|
||||||
|
infohash = infohash.replace('=', '$');
|
||||||
|
_config.remove(PROP_META_MAGNET_PREFIX + infohash);
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does not really delete on failure, that's the caller's responsibility.
|
* Does not really delete on failure, that's the caller's responsibility.
|
||||||
* Warning - does not validate announce URL - use TrackerClient.isValidAnnounce()
|
* Warning - does not validate announce URL - use TrackerClient.isValidAnnounce()
|
||||||
@ -1032,12 +1059,10 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//start magnets
|
|
||||||
|
|
||||||
|
|
||||||
// here because we need to delay until I2CP is up
|
// here because we need to delay until I2CP is up
|
||||||
// although the user will see the default until then
|
// although the user will see the default until then
|
||||||
getBWLimit();
|
getBWLimit();
|
||||||
|
boolean doMagnets = true;
|
||||||
while (true) {
|
while (true) {
|
||||||
File dir = getDataDir();
|
File dir = getDataDir();
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
@ -1050,6 +1075,10 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
_log.error("Error in the DirectoryMonitor", e);
|
_log.error("Error in the DirectoryMonitor", e);
|
||||||
}
|
}
|
||||||
|
if (doMagnets) {
|
||||||
|
addMagnets();
|
||||||
|
doMagnets = false;
|
||||||
|
}
|
||||||
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1090,9 +1119,10 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
* and save the data to disk.
|
* and save the data to disk.
|
||||||
* A Snark.CompleteListener method.
|
* A Snark.CompleteListener method.
|
||||||
*
|
*
|
||||||
|
* @return the new name for the torrent or null on error
|
||||||
* @since 0.8.4
|
* @since 0.8.4
|
||||||
*/
|
*/
|
||||||
public void gotMetaInfo(Snark snark) {
|
public String gotMetaInfo(Snark snark) {
|
||||||
MetaInfo meta = snark.getMetaInfo();
|
MetaInfo meta = snark.getMetaInfo();
|
||||||
Storage storage = snark.getStorage();
|
Storage storage = snark.getStorage();
|
||||||
if (meta != null && storage != null) {
|
if (meta != null && storage != null) {
|
||||||
@ -1100,23 +1130,51 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
if (rejectMessage != null) {
|
if (rejectMessage != null) {
|
||||||
addMessage(rejectMessage);
|
addMessage(rejectMessage);
|
||||||
snark.stopTorrent();
|
snark.stopTorrent();
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
saveTorrentStatus(meta, storage.getBitField(), null); // no file priorities
|
saveTorrentStatus(meta, storage.getBitField(), null); // no file priorities
|
||||||
String name = (new File(getDataDir(), storage.getBaseName() + ".torrent")).getAbsolutePath();
|
String name = (new File(getDataDir(), storage.getBaseName() + ".torrent")).getAbsolutePath();
|
||||||
try {
|
try {
|
||||||
synchronized (_snarks) {
|
synchronized (_snarks) {
|
||||||
locked_writeMetaInfo(meta, name);
|
locked_writeMetaInfo(meta, name);
|
||||||
|
// put it in the list under the new name
|
||||||
|
_snarks.remove(snark.getName());
|
||||||
|
_snarks.put(name, snark);
|
||||||
}
|
}
|
||||||
|
_magnets.remove(snark.getName());
|
||||||
|
removeMagnetStatus(snark.getInfoHash());
|
||||||
|
addMessage(_("Metainfo received for {0}", snark.getName()));
|
||||||
|
addMessage(_("Starting up torrent {0}", storage.getBaseName()));
|
||||||
|
return name;
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
addMessage(_("Failed to copy torrent file to {0}", name));
|
addMessage(_("Failed to copy torrent file to {0}", name));
|
||||||
_log.error("Failed to write torrent file", ioe);
|
_log.error("Failed to write torrent file", ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// End Snark.CompleteListeners
|
// End Snark.CompleteListeners
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all magnets from the config file
|
||||||
|
* @since 0.8.4
|
||||||
|
*/
|
||||||
|
private void addMagnets() {
|
||||||
|
for (Object o : _config.keySet()) {
|
||||||
|
String k = (String) o;
|
||||||
|
if (k.startsWith(PROP_META_MAGNET_PREFIX)) {
|
||||||
|
String b64 = k.substring(PROP_META_MAGNET_PREFIX.length());
|
||||||
|
b64 = b64.replace('$', '=');
|
||||||
|
byte[] ih = Base64.decode(b64);
|
||||||
|
// ignore value
|
||||||
|
if (ih != null && ih.length == 20)
|
||||||
|
addMagnet("Magnet: " + I2PSnarkUtil.toHex(ih), ih, false);
|
||||||
|
// else remove from config?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void monitorTorrents(File dir) {
|
private void monitorTorrents(File dir) {
|
||||||
String fileNames[] = dir.list(TorrentFilenameFilter.instance());
|
String fileNames[] = dir.list(TorrentFilenameFilter.instance());
|
||||||
List<String> foundNames = new ArrayList(0);
|
List<String> foundNames = new ArrayList(0);
|
||||||
|
@ -173,7 +173,8 @@ public class TrackerClient extends I2PAppThread
|
|||||||
continue;
|
continue;
|
||||||
if (primary.startsWith("http://i2p/" + dest))
|
if (primary.startsWith("http://i2p/" + dest))
|
||||||
continue;
|
continue;
|
||||||
trackers.add(new Tracker(url, false));
|
// opentrackers are primary if we don't have primary
|
||||||
|
trackers.add(new Tracker(url, primary.equals("")));
|
||||||
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
|
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,7 +239,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
|
|
||||||
uploaded = coordinator.getUploaded();
|
uploaded = coordinator.getUploaded();
|
||||||
downloaded = coordinator.getDownloaded();
|
downloaded = coordinator.getDownloaded();
|
||||||
left = coordinator.getLeft();
|
left = coordinator.getLeft(); // -1 in magnet mode
|
||||||
|
|
||||||
// First time we got a complete download?
|
// First time we got a complete download?
|
||||||
String event;
|
String event;
|
||||||
@ -289,7 +290,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (left > 0) && (!completed) ) {
|
if ( (left != 0) && (!completed) ) {
|
||||||
// we only want to talk to new people if we need things
|
// we only want to talk to new people if we need things
|
||||||
// from them (duh)
|
// from them (duh)
|
||||||
List<Peer> ordered = new ArrayList(peers);
|
List<Peer> ordered = new ArrayList(peers);
|
||||||
@ -344,7 +345,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
// FIXME this needs to be in its own thread
|
// FIXME this needs to be in its own thread
|
||||||
if (_util.getDHT() != null && !stop) {
|
if (_util.getDHT() != null && !stop) {
|
||||||
int numwant;
|
int numwant;
|
||||||
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
||||||
numwant = 1;
|
numwant = 1;
|
||||||
else
|
else
|
||||||
numwant = _util.getMaxConnections();
|
numwant = _util.getMaxConnections();
|
||||||
@ -362,7 +363,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
List<Peer> peers = new ArrayList(hashes.size());
|
List<Peer> peers = new ArrayList(hashes.size());
|
||||||
for (Hash h : hashes) {
|
for (Hash h : hashes) {
|
||||||
PeerID pID = new PeerID(h.getData());
|
PeerID pID = new PeerID(h.getData());
|
||||||
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), meta));
|
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
|
||||||
}
|
}
|
||||||
Collections.shuffle(peers, r);
|
Collections.shuffle(peers, r);
|
||||||
Iterator<Peer> it = peers.iterator();
|
Iterator<Peer> it = peers.iterator();
|
||||||
@ -370,7 +371,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
Peer cur = it.next();
|
Peer cur = it.next();
|
||||||
if (coordinator.addPeer(cur)) {
|
if (coordinator.addPeer(cur)) {
|
||||||
int delay = DELAY_MUL;
|
int delay = DELAY_MUL;
|
||||||
delay *= ((int)cur.getPeerID().getAddress().calculateHash().toBase64().charAt(0)) % 10;
|
delay *= r.nextInt(10);
|
||||||
delay += DELAY_MIN;
|
delay += DELAY_MIN;
|
||||||
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
||||||
}
|
}
|
||||||
@ -418,6 +419,8 @@ public class TrackerClient extends I2PAppThread
|
|||||||
long downloaded, long left, String event)
|
long downloaded, long left, String event)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
// What do we send for left in magnet mode? Can we omit it?
|
||||||
|
long tleft = left >= 0 ? left : 1;
|
||||||
String s = tr.announce
|
String s = tr.announce
|
||||||
+ "?info_hash=" + infoHash
|
+ "?info_hash=" + infoHash
|
||||||
+ "&peer_id=" + peerID
|
+ "&peer_id=" + peerID
|
||||||
@ -425,10 +428,10 @@ public class TrackerClient extends I2PAppThread
|
|||||||
+ "&ip=" + _util.getOurIPString() + ".i2p"
|
+ "&ip=" + _util.getOurIPString() + ".i2p"
|
||||||
+ "&uploaded=" + uploaded
|
+ "&uploaded=" + uploaded
|
||||||
+ "&downloaded=" + downloaded
|
+ "&downloaded=" + downloaded
|
||||||
+ "&left=" + left
|
+ "&left=" + tleft
|
||||||
+ "&compact=1" // NOTE: opentracker will return 400 for &compact alone
|
+ "&compact=1" // NOTE: opentracker will return 400 for &compact alone
|
||||||
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
|
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
|
||||||
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
||||||
s += "&numwant=0";
|
s += "&numwant=0";
|
||||||
else
|
else
|
||||||
s += "&numwant=" + _util.getMaxConnections();
|
s += "&numwant=" + _util.getMaxConnections();
|
||||||
@ -445,7 +448,7 @@ public class TrackerClient extends I2PAppThread
|
|||||||
in = new FileInputStream(fetched);
|
in = new FileInputStream(fetched);
|
||||||
|
|
||||||
TrackerInfo info = new TrackerInfo(in, snark.getID(),
|
TrackerInfo info = new TrackerInfo(in, snark.getID(),
|
||||||
snark.getMetaInfo());
|
snark.getInfoHash(), snark.getMetaInfo());
|
||||||
_util.debug("TrackerClient response: " + info, Snark.INFO);
|
_util.debug("TrackerClient response: " + info, Snark.INFO);
|
||||||
|
|
||||||
String failure = info.getFailureReason();
|
String failure = info.getFailureReason();
|
||||||
|
@ -46,19 +46,20 @@ public class TrackerInfo
|
|||||||
private int complete;
|
private int complete;
|
||||||
private int incomplete;
|
private int incomplete;
|
||||||
|
|
||||||
public TrackerInfo(InputStream in, byte[] my_id, MetaInfo metainfo)
|
/** @param metainfo may be null */
|
||||||
|
public TrackerInfo(InputStream in, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
this(new BDecoder(in), my_id, metainfo);
|
this(new BDecoder(in), my_id, infohash, metainfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackerInfo(BDecoder be, byte[] my_id, MetaInfo metainfo)
|
private TrackerInfo(BDecoder be, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
this(be.bdecodeMap().getMap(), my_id, metainfo);
|
this(be.bdecodeMap().getMap(), my_id, infohash, metainfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackerInfo(Map m, byte[] my_id, MetaInfo metainfo)
|
private TrackerInfo(Map m, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
BEValue reason = (BEValue)m.get("failure reason");
|
BEValue reason = (BEValue)m.get("failure reason");
|
||||||
@ -84,10 +85,10 @@ public class TrackerInfo
|
|||||||
Set<Peer> p;
|
Set<Peer> p;
|
||||||
try {
|
try {
|
||||||
// One big string (the official compact format)
|
// One big string (the official compact format)
|
||||||
p = getPeers(bePeers.getBytes(), my_id, metainfo);
|
p = getPeers(bePeers.getBytes(), my_id, infohash, metainfo);
|
||||||
} catch (InvalidBEncodingException ibe) {
|
} catch (InvalidBEncodingException ibe) {
|
||||||
// List of Dictionaries or List of Strings
|
// List of Dictionaries or List of Strings
|
||||||
p = getPeers(bePeers.getList(), my_id, metainfo);
|
p = getPeers(bePeers.getList(), my_id, infohash, metainfo);
|
||||||
}
|
}
|
||||||
peers = p;
|
peers = p;
|
||||||
}
|
}
|
||||||
@ -123,7 +124,7 @@ public class TrackerInfo
|
|||||||
******/
|
******/
|
||||||
|
|
||||||
/** List of Dictionaries or List of Strings */
|
/** List of Dictionaries or List of Strings */
|
||||||
private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, MetaInfo metainfo)
|
private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Set<Peer> peers = new HashSet(l.size());
|
Set<Peer> peers = new HashSet(l.size());
|
||||||
@ -144,7 +145,7 @@ public class TrackerInfo
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
peers.add(new Peer(peerID, my_id, metainfo.getInfoHash(), metainfo));
|
peers.add(new Peer(peerID, my_id, infohash, metainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
return peers;
|
return peers;
|
||||||
@ -156,7 +157,7 @@ public class TrackerInfo
|
|||||||
* One big string of concatenated 32-byte hashes
|
* One big string of concatenated 32-byte hashes
|
||||||
* @since 0.8.1
|
* @since 0.8.1
|
||||||
*/
|
*/
|
||||||
private static Set<Peer> getPeers(byte[] l, byte[] my_id, MetaInfo metainfo)
|
private static Set<Peer> getPeers(byte[] l, byte[] my_id, byte[] infohash, MetaInfo metainfo)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
int count = l.length / HASH_LENGTH;
|
int count = l.length / HASH_LENGTH;
|
||||||
@ -172,7 +173,7 @@ public class TrackerInfo
|
|||||||
// won't happen
|
// won't happen
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
peers.add(new Peer(peerID, my_id, metainfo.getInfoHash(), metainfo));
|
peers.add(new Peer(peerID, my_id, infohash, metainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
return peers;
|
return peers;
|
||||||
|
@ -24,6 +24,8 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.i2p.data.Base64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds different types that a bencoded byte array can represent.
|
* Holds different types that a bencoded byte array can represent.
|
||||||
* You need to call the correct get method to get the correct java
|
* You need to call the correct get method to get the correct java
|
||||||
@ -180,10 +182,14 @@ public class BEValue
|
|||||||
{
|
{
|
||||||
byte[] bs = (byte[])value;
|
byte[] bs = (byte[])value;
|
||||||
// XXX - Stupid heuristic... and not UTF-8
|
// XXX - Stupid heuristic... and not UTF-8
|
||||||
if (bs.length <= 12)
|
//if (bs.length <= 12)
|
||||||
valueString = new String(bs);
|
// valueString = new String(bs);
|
||||||
|
//else
|
||||||
|
// valueString = "bytes:" + bs.length;
|
||||||
|
if (bs.length <= 32)
|
||||||
|
valueString = bs.length + " bytes: " + Base64.encode(bs);
|
||||||
else
|
else
|
||||||
valueString = "bytes:" + bs.length;
|
valueString = bs.length + " bytes";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
valueString = value.toString();
|
valueString = value.toString();
|
||||||
|
@ -1314,6 +1314,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(" ");
|
out.write(" ");
|
||||||
out.write(renderOptions(0, 4, options.remove("outbound.length"), "outbound.length", HOP));
|
out.write(renderOptions(0, 4, options.remove("outbound.length"), "outbound.length", HOP));
|
||||||
|
|
||||||
|
if (!_context.isRouterContext()) {
|
||||||
out.write("<tr><td>");
|
out.write("<tr><td>");
|
||||||
out.write(_("I2CP host"));
|
out.write(_("I2CP host"));
|
||||||
out.write(": <td><input type=\"text\" name=\"i2cpHost\" value=\""
|
out.write(": <td><input type=\"text\" name=\"i2cpHost\" value=\""
|
||||||
@ -1323,6 +1324,7 @@ public class I2PSnarkServlet extends Default {
|
|||||||
out.write(_("I2CP port"));
|
out.write(_("I2CP port"));
|
||||||
out.write(": <td><input type=\"text\" name=\"i2cpPort\" class=\"r\" value=\"" +
|
out.write(": <td><input type=\"text\" name=\"i2cpPort\" class=\"r\" value=\"" +
|
||||||
+ _manager.util().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" > <br>\n");
|
+ _manager.util().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" > <br>\n");
|
||||||
|
}
|
||||||
|
|
||||||
StringBuilder opts = new StringBuilder(64);
|
StringBuilder opts = new StringBuilder(64);
|
||||||
for (Iterator iter = options.entrySet().iterator(); iter.hasNext(); ) {
|
for (Iterator iter = options.entrySet().iterator(); iter.hasNext(); ) {
|
||||||
|
Reference in New Issue
Block a user