package com.limegroup.gnutella.messages.vendor; import java.io.IOException; import java.io.OutputStream; import com.limegroup.gnutella.ByteOrder; import com.limegroup.gnutella.GUID; import com.limegroup.gnutella.messages.BadPacketException; import com.limegroup.gnutella.statistics.SentMessageStatHandler; /** In Vendor Message parlance, the "message type" of this VMP is "LIME/11". * This message acknowledges (ACKS) the guid contained in the message (i.e. A * sends B a message with GUID g, B can acknowledge this message by sending a * LimeACKVendorMessage to A with GUID g). It also contains the amount of * results the client wants. * * 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. * EXCEPTION: Version 1 is NEVER accepted. Only version's 2 and above are * recognized. * * 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 LimeACKVendorMessage extends VendorMessage { public static final int VERSION = 2; /** * Constructs a new LimeACKVendorMessage with data from the network. */ LimeACKVendorMessage(byte[] guid, byte ttl, byte hops, int version, byte[] payload) throws BadPacketException { super(guid, ttl, hops, F_LIME_VENDOR_ID, F_LIME_ACK, version, payload); if (getVersion() == 1) throw new BadPacketException("UNSUPPORTED OLD VERSION"); if (getPayload().length < 1) throw new BadPacketException("UNSUPPORTED PAYLOAD LENGTH: " + getPayload().length); if ((getVersion() == 2) && (getPayload().length != 1)) throw new BadPacketException("VERSION 2 UNSUPPORTED PAYLOAD LEN: " + getPayload().length); } /** * Constructs a new LimeACKVendorMessage to be sent out. * @param numResults The number of results (0-255 inclusive) that you want * for this query. If you want more than 255 just send 255. * @param replyGUID The guid of the original query/reply that you want to * send reply info for. */ public LimeACKVendorMessage(GUID replyGUID, int numResults) { super(F_LIME_VENDOR_ID, F_LIME_ACK, VERSION, derivePayload(numResults)); setGUID(replyGUID); } /** @return an int (0-255) representing the amount of results that a host * wants for a given query (as specified by the guid of this message). */ public int getNumResults() { return ByteOrder.ubyte2int(getPayload()[0]); } /** * Constructs the payload for a LimeACKVendorMessage with the given * number of results. */ private static byte[] derivePayload(int numResults) { if ((numResults < 0) || (numResults > 255)) throw new IllegalArgumentException("Number of results too big: " + numResults); byte[] payload = new byte[1]; byte[] bytes = new byte[2]; ByteOrder.short2leb((short) numResults, bytes, 0); payload[0] = bytes[0]; return payload; } public boolean equals(Object other) { if (other instanceof LimeACKVendorMessage) { GUID myGuid = new GUID(getGUID()); GUID otherGuid = new GUID(((VendorMessage) other).getGUID()); int otherResults = ((LimeACKVendorMessage) other).getNumResults(); return ((myGuid.equals(otherGuid)) && (getNumResults() == otherResults) && super.equals(other)); } return false; } /** Overridden purely for stats handling. */ protected void writePayload(OutputStream out) throws IOException { super.writePayload(out); SentMessageStatHandler.UDP_LIME_ACK.addMessage(this); } /** Overridden purely for stats handling. */ public void recordDrop() { super.recordDrop(); } }