forked from I2P_Developers/i2p.i2p
i2psnark: Initial support for ut_comment, no UI yet
This commit is contained in:
@@ -14,6 +14,8 @@ import net.i2p.util.Log;
|
||||
import org.klomp.snark.bencode.BDecoder;
|
||||
import org.klomp.snark.bencode.BEncoder;
|
||||
import org.klomp.snark.bencode.BEValue;
|
||||
import org.klomp.snark.comments.Comment;
|
||||
import org.klomp.snark.comments.CommentSet;
|
||||
|
||||
/**
|
||||
* REF: BEP 10 Extension Protocol
|
||||
@@ -31,6 +33,10 @@ abstract class ExtensionHandler {
|
||||
public static final int ID_DHT = 3;
|
||||
/** not using the option bit since the compact format is different */
|
||||
public static final String TYPE_DHT = "i2p_dht";
|
||||
/** @since 0.9.31 */
|
||||
public static final int ID_COMMENT = 4;
|
||||
/** @since 0.9.31 */
|
||||
public static final String TYPE_COMMENT = "ut_comment";
|
||||
/** Pieces * SHA1 Hash length, + 25% extra for file names, bencoding overhead, etc */
|
||||
private static final int MAX_METADATA_SIZE = Storage.MAX_PIECES * 20 * 5 / 4;
|
||||
private static final int PARALLEL_REQUESTS = 3;
|
||||
@@ -40,9 +46,10 @@ abstract class ExtensionHandler {
|
||||
* @param metasize -1 if unknown
|
||||
* @param pexAndMetadata advertise these capabilities
|
||||
* @param dht advertise DHT capability
|
||||
* @param comment advertise ut_comment capability
|
||||
* @return bencoded outgoing handshake message
|
||||
*/
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht, boolean uploadOnly) {
|
||||
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht, boolean uploadOnly, boolean comment) {
|
||||
Map<String, Object> handshake = new HashMap<String, Object>();
|
||||
Map<String, Integer> m = new HashMap<String, Integer>();
|
||||
if (pexAndMetadata) {
|
||||
@@ -54,6 +61,9 @@ abstract class ExtensionHandler {
|
||||
if (dht) {
|
||||
m.put(TYPE_DHT, Integer.valueOf(ID_DHT));
|
||||
}
|
||||
if (comment) {
|
||||
m.put(TYPE_COMMENT, Integer.valueOf(ID_COMMENT));
|
||||
}
|
||||
// include the map even if empty so the far-end doesn't NPE
|
||||
handshake.put("m", m);
|
||||
handshake.put("p", Integer.valueOf(TrackerClient.PORT));
|
||||
@@ -77,6 +87,8 @@ abstract class ExtensionHandler {
|
||||
handlePEX(peer, listener, bs, log);
|
||||
else if (id == ID_DHT)
|
||||
handleDHT(peer, listener, bs, log);
|
||||
else if (id == ID_COMMENT)
|
||||
handleComment(peer, listener, bs, log);
|
||||
else if (log.shouldLog(Log.INFO))
|
||||
log.info("Unknown extension msg " + id + " from " + peer);
|
||||
}
|
||||
@@ -430,4 +442,113 @@ abstract class ExtensionHandler {
|
||||
// log.info("DHT msg exception to " + peer, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle comment request and response
|
||||
*
|
||||
* Ref: https://blinkenlights.ch/ccms/software/bittorrent.html
|
||||
* Ref: https://github.com/adrian-bl/bitflu/blob/3cb7fe887dbdea8132e4fa36fbbf5f26cf992db3/plugins/Bitflu/20_DownloadBitTorrent.pm#L3403
|
||||
* @since 0.9.31
|
||||
*/
|
||||
private static void handleComment(Peer peer, PeerListener listener, byte[] bs, Log log) {
|
||||
if (log.shouldLog(Log.DEBUG))
|
||||
log.debug("Got comment msg from " + peer);
|
||||
try {
|
||||
InputStream is = new ByteArrayInputStream(bs);
|
||||
BDecoder dec = new BDecoder(is);
|
||||
BEValue bev = dec.bdecodeMap();
|
||||
Map<String, BEValue> map = bev.getMap();
|
||||
int type = map.get("msg_type").getInt();
|
||||
if (type == 0) {
|
||||
// request
|
||||
int num = 20;
|
||||
BEValue b = map.get("num");
|
||||
if (b != null)
|
||||
num = b.getInt();
|
||||
listener.gotCommentReq(peer, num);
|
||||
} else if (type == 1) {
|
||||
// response
|
||||
List<BEValue> list = map.get("comments").getList();
|
||||
if (list.isEmpty())
|
||||
return;
|
||||
List<Comment> comments = new ArrayList<Comment>(list.size());
|
||||
long now = I2PAppContext.getGlobalContext().clock().now();
|
||||
for (BEValue li : list) {
|
||||
Map<String, BEValue> m = li.getMap();
|
||||
String owner = m.get("owner").getString();
|
||||
String text = m.get("text").getString();
|
||||
int rating = m.get("like").getInt();
|
||||
long time = now - (Math.max(0, m.get("timestamp").getInt()) * 1000L);
|
||||
Comment c = new Comment(text, owner, rating, time, false);
|
||||
comments.add(c);
|
||||
}
|
||||
listener.gotComments(peer, comments);
|
||||
} else {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Unknown comment msg type " + type + " from " + peer);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info("Comment msg exception from " + peer, e);
|
||||
//peer.disconnect(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[] COMMENTS_FILTER = new byte[64];
|
||||
|
||||
/**
|
||||
* Send comment request
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public static void sendCommentReq(Peer peer, int num) {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("msg_type", Integer.valueOf(0));
|
||||
map.put("num", Integer.valueOf(num));
|
||||
map.put("filter", COMMENTS_FILTER);
|
||||
byte[] payload = BEncoder.bencode(map);
|
||||
try {
|
||||
int hisMsgCode = peer.getHandshakeMap().get("m").getMap().get(TYPE_COMMENT).getInt();
|
||||
peer.sendExtension(hisMsgCode, payload);
|
||||
} catch (Exception e) {
|
||||
// NPE, no caps
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send comments
|
||||
* Caller must sync on comments
|
||||
* @param num max to send
|
||||
* @param comments non-null
|
||||
* @since 0.9.31
|
||||
*/
|
||||
public static void locked_sendComments(Peer peer, int num, CommentSet comments) {
|
||||
int toSend = Math.min(num, comments.size());
|
||||
if (toSend <= 0)
|
||||
return;
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("msg_type", Integer.valueOf(1));
|
||||
List<Object> lc = new ArrayList<Object>(toSend);
|
||||
long now = I2PAppContext.getGlobalContext().clock().now();
|
||||
int i = 0;
|
||||
for (Comment c : comments) {
|
||||
if (i++ >= toSend)
|
||||
break;
|
||||
Map<String, Object> mc = new HashMap<String, Object>();
|
||||
String s = c.getName();
|
||||
mc.put("owner", s != null ? s : "");
|
||||
s = c.getText();
|
||||
mc.put("text", s != null ? s : "");
|
||||
mc.put("like", Integer.valueOf(c.getRating()));
|
||||
mc.put("timestamp", Long.valueOf((now - c.getTime()) / 1000L));
|
||||
lc.add(mc);
|
||||
}
|
||||
map.put("comments", lc);
|
||||
byte[] payload = BEncoder.bencode(map);
|
||||
try {
|
||||
int hisMsgCode = peer.getHandshakeMap().get("m").getMap().get(TYPE_COMMENT).getInt();
|
||||
peer.sendExtension(hisMsgCode, payload);
|
||||
} catch (Exception e) {
|
||||
// NPE, no caps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user