package com.limegroup.gnutella.messages.vendor; import java.io.IOException; import java.io.OutputStream; import org.limewire.core.settings.SearchSettings; import org.limewire.io.GUID; import org.limewire.util.ByteUtils; import com.limegroup.gnutella.messages.BadPacketException; /** In Vendor Message parlance, the "message type" of this VMP is "LIME/12". * This message contains a unsigned byte (1-255) that tells you how many * results the sending host has for the guid of a query (the guid of this * message is the same as the original query). The recieving host can ACK * this message with a LimeACKVendorMessage to actually recieve the replies. * * This message must maintain backwards compatibility between successive * versions. This entails that any new features would grow the message * outward but shouldn't change the meaning of older fields. This could lead * to some issues (i.e. abandoning fields does not allow for older fields to * be reused) but since we don't expect major changes this is probably OK. * * Note that this behavior of maintaining backwards compatiblity is really * only necessary for UDP messages since in the UDP case there is probably no * MessagesSupportedVM exchange. */ public final class ReplyNumberVendorMessage extends AbstractVendorMessage { public static final int OLD_VERSION = 2; public static final int VERSION = 3; /** * whether we can receive unsolicited udp */ protected static final byte UNSOLICITED=0x1; /** * Constructs a new ReplyNumberVendorMessages with data from the network. */ ReplyNumberVendorMessage(byte[] guid, byte ttl, byte hops, int version, byte[] payload, Network network) throws BadPacketException { super(guid, ttl, hops, F_LIME_VENDOR_ID, F_REPLY_NUMBER, version, payload, network); if (version < OLD_VERSION) throw new BadPacketException("ancient version"); // only allow current version to come from network // unless OOBv2 is allowed if (version < VERSION) { if (SearchSettings.DISABLE_OOB_V2.getBoolean()) throw new BadPacketException("OOB v2 not allowed"); if (getPayload().length != 2) throw new BadPacketException("v2 message too large"); } // loosen the condition on the message size to allow this message version // to have a GGEP in the future if (getPayload().length < 2) throw new BadPacketException("VERSION " + version+" UNSUPPORTED PAYLOAD LEN: " + getPayload().length); } /** * Constructs a new ReplyNumberVendorMessage to be sent out. * @param numResults The number of results (1-255 inclusive) that you have * for this query. If you have more than 255 just send 255. * @param replyGUID The guid of the original query/reply that you want to * send reply info for. */ ReplyNumberVendorMessage(GUID replyGUID, int version, int numResults, boolean canReceiveUnsolicited) { super(F_LIME_VENDOR_ID, F_REPLY_NUMBER, version, derivePayload(numResults, canReceiveUnsolicited)); setGUID(replyGUID); } /** Constructs the payload from the desired number of results. */ private static byte[] derivePayload(int numResults, boolean canReceiveUnsolicited) { if ((numResults < 1) || (numResults > 255)) throw new IllegalArgumentException("Number of results too big: " + numResults); byte[] bytes = new byte[2]; ByteUtils.short2leb((short) numResults, bytes, 0); bytes[1] = canReceiveUnsolicited ? ReplyNumberVendorMessage.UNSOLICITED : 0x0; return bytes; } /** @return an int (1-255) representing the amount of results that a host * for a given query (as specified by the guid of this message). */ public int getNumResults() { return ByteUtils.ubyte2int(getPayload()[0]); } public boolean canReceiveUnsolicited() { if (getVersion() ==1) return true; else return (getPayload()[1] & UNSOLICITED) == UNSOLICITED; } @Override public boolean equals(Object other) { if (other instanceof ReplyNumberVendorMessage) { GUID myGuid = new GUID(getGUID()); GUID otherGuid = new GUID(((VendorMessage) other).getGUID()); int otherResults = ((ReplyNumberVendorMessage) other).getNumResults(); return ((myGuid.equals(otherGuid)) && (getNumResults() == otherResults) && super.equals(other)); } return false; } /** Overridden purely for stats handling. */ @Override protected void writePayload(OutputStream out) throws IOException { super.writePayload(out); } public boolean isOOBv3() { return getVersion() == VERSION; } @Override public String toString() { StringBuilder builder = new StringBuilder(super.toString()); builder.append(", num results: ").append(getNumResults()); builder.append(", canReceiveUnsolicited:").append(canReceiveUnsolicited()); return builder.toString(); } }