package edu.washington.cs.oneswarm.f2f.messaging;
import org.gudy.azureus2.core3.util.Base32;
import org.gudy.azureus2.core3.util.DirectByteBuffer;
import org.gudy.azureus2.core3.util.DirectByteBufferPool;
import com.aelitis.azureus.core.networkmanager.RawMessage;
import com.aelitis.azureus.core.peermanager.messaging.Message;
import com.aelitis.azureus.core.peermanager.messaging.MessageException;
public class OSF2FHandshake implements OSF2FMessage, RawMessage {
private String description = null;
private final byte version;
private byte[] reserved = null;
private DirectByteBuffer buffer;
private boolean noDelay = true;
/**
* Possible supported extensions
*/
// this includes directory tag lists
public static final byte SUPPORTS_EXTENDED_FILE_LISTS = 1;
public static final byte SUPPORTS_CHAT = 2;
public static final byte SUPPORTS_DHT_LOCATION_HS = 4;
public static final byte SUPPORTS_UDP = 8;
/**
* Protocol extensions we support in the current version. This is a very
* hacky way to do protocol versioning, but it's what we're using for now to
* maintain backwards compatibility. TODO: introduce proper versioning in a
* future handshake message as an option and then exchange an extended
* handshake message
*/
public final static byte[] OS_FLAGS = new byte[] {
SUPPORTS_EXTENDED_FILE_LISTS | SUPPORTS_CHAT | SUPPORTS_DHT_LOCATION_HS | SUPPORTS_UDP,
0, 0, 0, 0, 0,
0, 0 };
public final static byte MESSAGE_LENGTH = (byte) (1 + ONESWARM_PROTOCOL.length() + OS_FLAGS.length);
public OSF2FHandshake(byte _version, byte[] reserved) {
this.version = _version;
this.reserved = reserved;
}
public byte[] getFlags() {
return reserved;
}
@Override
public Message deserialize(DirectByteBuffer data, byte version) throws MessageException {
if (data == null) {
throw new MessageException("[" + getID() + "] decode error: data == null");
}
if (data.remaining(DirectByteBuffer.SS_MSG) != MESSAGE_LENGTH) {
throw new MessageException("[" + getID() + "] decode error: payload.remaining["
+ data.remaining(DirectByteBuffer.SS_MSG) + "] != " + MESSAGE_LENGTH);
}
byte len = data.get(DirectByteBuffer.SS_MSG);
if (len != (byte) ONESWARM_PROTOCOL.length()) {
throw new MessageException("[" + getID() + "] decode error: payload.get() != "
+ "(byte)PROTOCOL.length() " + "got " + len + " expected " + MESSAGE_LENGTH);
}
byte[] header = new byte[ONESWARM_PROTOCOL.getBytes().length];
data.get(DirectByteBuffer.SS_MSG, header);
if (!ONESWARM_PROTOCOL.equals(new String(header))
&& !SPD_HANDSHAKE.equals(new String(header))) {
throw new MessageException("[" + getID() + "] decode error: invalid protocol given: "
+ new String(header));
}
byte[] reserved = new byte[8];
data.get(DirectByteBuffer.SS_MSG, reserved);
// Log.log("successfully decoded OSF2F handshake");
data.returnToPool();
return new OSF2FHandshake(version, reserved);
}
@Override
public String getID() {
return OSF2FMessage.ID_OS_HANDSHAKE;
}
@Override
public byte[] getIDBytes() {
return OSF2FMessage.ID_OS_HANDSHAKE_BYTES;
}
@Override
public String getFeatureID() {
return OSF2FMessage.OS_FEATURE_ID;
}
@Override
public int getFeatureSubID() {
return OSF2FMessage.SUBID_OS_HANDSHAKE;
}
@Override
public int getType() {
return Message.TYPE_PROTOCOL_PAYLOAD;
}
@Override
public byte getVersion() {
return version;
};
@Override
public String getDescription() {
if (description == null) {
description = OSF2FMessage.ID_OS_HANDSHAKE + " flags: " + Base32.encode(reserved);
}
return description;
}
@Override
public void destroy() {
if (buffer != null)
buffer.returnToPool();
}
private void constructBuffer() {
if (buffer == null) {
buffer = DirectByteBufferPool.getBuffer(DirectByteBuffer.AL_MSG, MESSAGE_LENGTH);
buffer.put(DirectByteBuffer.SS_MSG, (byte) ONESWARM_PROTOCOL.length());
buffer.put(DirectByteBuffer.SS_MSG, ONESWARM_PROTOCOL.getBytes());
buffer.put(DirectByteBuffer.SS_MSG, reserved);
buffer.flip(DirectByteBuffer.SS_MSG);
}
}
@Override
public DirectByteBuffer[] getData() {
this.constructBuffer();
return new DirectByteBuffer[] { buffer };
}
@Override
public DirectByteBuffer[] getRawData() {
this.constructBuffer();
return new DirectByteBuffer[] { buffer };
}
@Override
public int getPriority() {
return RawMessage.PRIORITY_HIGH;
}
@Override
public boolean isNoDelay() {
return noDelay;
}
@Override
public Message[] messagesToRemove() {
return null;
}
@Override
public Message getBaseMessage() {
return this;
}
@Override
public void setNoDelay() {
noDelay = true;
}
@Override
public int getMessageSize() {
return MESSAGE_LENGTH;
}
}