commit
7156f6df98
@ -0,0 +1,52 @@
|
||||
package protocolP2P;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import tools.BytesArrayTools;
|
||||
|
||||
/** HashAlgorithm enum.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public enum HashAlgorithm {
|
||||
SHA512("SHA-512"),
|
||||
MD5("MD5");
|
||||
|
||||
private String name;
|
||||
/* To be able to convert name to enum */
|
||||
private static final Map<String, HashAlgorithm> BY_NAME = new HashMap<>();
|
||||
/* Initialization of HashMap */
|
||||
static {
|
||||
for (HashAlgorithm h: values()) {
|
||||
assert !BY_NAME.containsKey(h.name) : "Duplicate in " + HashAlgorithm.class.getCanonicalName();
|
||||
BY_NAME.put(h.name, h);
|
||||
}
|
||||
}
|
||||
|
||||
private HashAlgorithm(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/** Gives enum from name.
|
||||
* @param name name of the hash
|
||||
* @return enum element
|
||||
* @throws InterlanError
|
||||
*/
|
||||
protected static HashAlgorithm fromName(String name) throws InternalError {
|
||||
HashAlgorithm h = BY_NAME.get(BytesArrayTools.cleanStrings(name));
|
||||
assert h != null : "Bad algorithm name: " + name;
|
||||
if (h == null) {
|
||||
throw new InternalError();
|
||||
}
|
||||
return h;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,165 @@
|
||||
package protocolP2P;
|
||||
import protocolP2P.Payload;
|
||||
import protocolP2P.HashAlgorithm;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import exception.TransmissionError;
|
||||
import exception.SizeError;
|
||||
import exception.ProtocolError;
|
||||
import exception.InternalError;
|
||||
import tools.BytesArrayTools;
|
||||
|
||||
|
||||
/** Representation of payload for hash request.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class HashRequest extends Payload {
|
||||
private String filename;
|
||||
private HashAlgorithm[] algoList;
|
||||
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
|
||||
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
|
||||
|
||||
/** Constructor (typically used by the server) with a filename parameter.
|
||||
* @param filename name of the file to download. Must not be empty.
|
||||
* @param algoList List of hash algorithms used
|
||||
* @throws InternalError
|
||||
*
|
||||
*/
|
||||
public HashRequest(String filename, HashAlgorithm[] algoList) throws InternalError {
|
||||
super(RequestResponseCode.HASH_REQUEST);
|
||||
/* assert to help debugging */
|
||||
assert filename.length() != 0 : "Size of filename in HashRequest must not be empty";
|
||||
if (filename.length() == 0) {
|
||||
throw new InternalError();
|
||||
}
|
||||
this.filename = filename;
|
||||
assert algoList.length != 0 : "Hash algorithms list must not be empty";
|
||||
if (algoList.length == 0) {
|
||||
throw new InternalError();
|
||||
}
|
||||
this.algoList = algoList;
|
||||
}
|
||||
|
||||
/** Constructor (typically used by client) with a byte[] parameter containing the Packet received.
|
||||
* @param packet the full Packet received
|
||||
* @throws SizeError
|
||||
* @throws InternalError
|
||||
* @throws ProtocolError
|
||||
* @throws TransmissionError
|
||||
*/
|
||||
protected HashRequest(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
|
||||
super(packet);
|
||||
/* assert to help debugging */
|
||||
assert requestResponseCode == RequestResponseCode.HASH_REQUEST : "HashRequest subclass is incompatible with this Packet, request/response code must be checked before using this constructor";
|
||||
/* InternalErrorException */
|
||||
if (requestResponseCode!= RequestResponseCode.HASH_REQUEST) {
|
||||
throw new InternalError();
|
||||
}
|
||||
/* Read filename */
|
||||
int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
|
||||
try {
|
||||
filename = new String(packet, FILENAME_POSITION, filenameSize, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
int size = getPayloadSize(packet);
|
||||
try {
|
||||
String[] l = (new String(packet, FILENAME_POSITION + filenameSize, size, "UTF-8")).split("\n");
|
||||
int i = 0;
|
||||
algoList = new HashAlgorithm[l.length];
|
||||
for(String algo : l) {
|
||||
algoList[i] = HashAlgorithm.fromName(algo);
|
||||
i++;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a byte[] containing Packet with padding.
|
||||
* This Packet is still incomplete and should not be send directly.
|
||||
* ProtocolP2PPacket will use this method to generate the complete Packet.
|
||||
* @return Packet with padding
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected byte[] toPacket() throws InternalError {
|
||||
// compute size
|
||||
int filenameSize = filename.length();
|
||||
int size = FILENAME_POSITION + filenameSize;
|
||||
for (HashAlgorithm h : algoList) {
|
||||
if (h == null) {
|
||||
continue;
|
||||
}
|
||||
size += h.getName().length();
|
||||
size += 1; // size for '\n'
|
||||
}
|
||||
size -=1; // remove trailing '\n'
|
||||
byte[] packet = new byte[size + 1]; // java initialize all to zero
|
||||
|
||||
// set request/response code
|
||||
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||
|
||||
// set Payload size
|
||||
setPayloadSize(size - FILENAME_SIZE_POSITION, packet);
|
||||
|
||||
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
|
||||
|
||||
// Write filename
|
||||
int bCount = FILENAME_POSITION;
|
||||
try {
|
||||
byte[] sb = filename.getBytes("UTF-8");
|
||||
for(byte b : sb) {
|
||||
packet[bCount] = b;
|
||||
bCount += 1;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
boolean firstIter = true;
|
||||
for(HashAlgorithm h : algoList) {
|
||||
if (h == null) {
|
||||
continue;
|
||||
}
|
||||
String s = h.getName();
|
||||
if (!firstIter) {
|
||||
firstIter = false;
|
||||
try {
|
||||
packet[bCount] = "\n".getBytes("UTF-8")[0]; // separator
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
bCount += 1;
|
||||
}
|
||||
// Copy algoname
|
||||
try {
|
||||
byte[] sb = s.getBytes("UTF-8");
|
||||
for(byte b : sb) {
|
||||
packet[bCount] = b;
|
||||
bCount += 1;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/** AlgoList getter.
|
||||
* @return List of HashAlgorithms
|
||||
*/
|
||||
public HashAlgorithm[] getAlgoList() {
|
||||
return algoList;
|
||||
}
|
||||
|
||||
/** Filename getter.
|
||||
* @return filename
|
||||
*/
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
package protocolP2P;
|
||||
import protocolP2P.Payload;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import exception.TransmissionError;
|
||||
import exception.SizeError;
|
||||
import exception.ProtocolError;
|
||||
import exception.InternalError;
|
||||
import tools.BytesArrayTools;
|
||||
|
||||
|
||||
/** Representation of payload for hash response.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class HashResponse extends Payload {
|
||||
private String filename;
|
||||
private Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
|
||||
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
|
||||
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
|
||||
|
||||
/** Constructor (typically used by the server) with a filename parameter.
|
||||
* @param filename name of the file to download. Must not be empty.
|
||||
* @param hashes HashMap containing hashes for file.
|
||||
* @throws InternalError
|
||||
*
|
||||
*/
|
||||
public HashResponse(String filename, Map<HashAlgorithm, byte[]> hashes) throws InternalError {
|
||||
super(RequestResponseCode.HASH_RESPONSE);
|
||||
/* assert to help debugging */
|
||||
assert filename.length() != 0 : "Size of filename in HashResponse must not be empty";
|
||||
if (filename.length() == 0) {
|
||||
throw new InternalError();
|
||||
}
|
||||
this.filename = filename;
|
||||
|
||||
// initialize hashes
|
||||
assert !hashes.isEmpty() : "At least 1 hash must be provided";
|
||||
if (hashes.isEmpty()) {
|
||||
throw new InternalError();
|
||||
}
|
||||
this.hashes = hashes;
|
||||
}
|
||||
|
||||
/** Constructor (typically used by client) with a byte[] parameter containing the Packet received.
|
||||
* @param packet the full Packet received
|
||||
* @throws SizeError
|
||||
* @throws InternalError
|
||||
* @throws ProtocolError
|
||||
* @throws TransmissionError
|
||||
*/
|
||||
protected HashResponse(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
|
||||
super(packet);
|
||||
/* assert to help debugging */
|
||||
assert requestResponseCode == RequestResponseCode.HASH_RESPONSE : "HashResponse subclass is incompatible with this Packet, request/response code must be checked before using this constructor";
|
||||
/* InternalErrorException */
|
||||
if (requestResponseCode!= RequestResponseCode.HASH_RESPONSE) {
|
||||
throw new InternalError();
|
||||
}
|
||||
/* Read filename */
|
||||
int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
|
||||
try {
|
||||
filename = new String(packet, FILENAME_POSITION, filenameSize, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
int size = getPayloadSize(packet);
|
||||
int start = FILENAME_POSITION + filenameSize;
|
||||
do {
|
||||
int algoNameSize = BytesArrayTools.readInt(packet, start);
|
||||
start +=4;
|
||||
try {
|
||||
String algoName = new String(packet, start, algoNameSize, "UTF-8");
|
||||
start += algoNameSize;
|
||||
int hashSize = BytesArrayTools.readInt(packet, start);
|
||||
start += 4;
|
||||
if (hashSize != 0) {
|
||||
byte[] b = new byte[hashSize];
|
||||
for (int i=0;i<hashSize;i++) {
|
||||
b[i] = packet[start+i];
|
||||
}
|
||||
hashes.put(HashAlgorithm.fromName(algoName), b);
|
||||
}
|
||||
start += hashSize;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
} while (start < size);
|
||||
|
||||
}
|
||||
|
||||
/** Returns a byte[] containing Packet with padding.
|
||||
* This Packet is still incomplete and should not be send directly.
|
||||
* ProtocolP2PPacket will use this method to generate the complete Packet.
|
||||
* @return Packet with padding
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected byte[] toPacket() throws InternalError {
|
||||
// compute size
|
||||
int filenameSize = filename.length();
|
||||
int size = FILENAME_POSITION + filenameSize;
|
||||
for (HashAlgorithm h : hashes.keySet()) {
|
||||
size += 4 + h.getName().length();
|
||||
}
|
||||
for (byte[] s : hashes.values()) {
|
||||
size += 4 + s.length;
|
||||
}
|
||||
byte[] packet = new byte[size + 1]; // java initialize all to zero
|
||||
|
||||
// set request/response code
|
||||
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||
|
||||
// set Payload size
|
||||
setPayloadSize(size - FILENAME_SIZE_POSITION, packet);
|
||||
|
||||
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
|
||||
|
||||
// Write filename
|
||||
int bCount = FILENAME_POSITION;
|
||||
try {
|
||||
byte[] sb = filename.getBytes("UTF-8");
|
||||
for(byte b : sb) {
|
||||
packet[bCount] = b;
|
||||
bCount += 1;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
for(HashAlgorithm h : hashes.keySet()) {
|
||||
String s = h.getName();
|
||||
BytesArrayTools.write(packet, bCount, (int)s.length());
|
||||
bCount += 4;
|
||||
// Copy algoname
|
||||
try {
|
||||
byte[] sb = s.getBytes("UTF-8");
|
||||
for(byte b : sb) {
|
||||
packet[bCount] = b;
|
||||
bCount += 1;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
byte[] hashb = hashes.get(HashAlgorithm.fromName(s));
|
||||
if (hashb.length == 0) {
|
||||
BytesArrayTools.write(packet, bCount, (int)0);
|
||||
bCount += 4;
|
||||
} else {
|
||||
BytesArrayTools.write(packet, bCount, (int)hashb.length);
|
||||
bCount +=4;
|
||||
// copy hash
|
||||
for(byte b : hashb) {
|
||||
packet[bCount] = b;
|
||||
bCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/** hash getter
|
||||
* @param hashAlgo HashAlgorithm to return hash from
|
||||
* @return hash
|
||||
*/
|
||||
public byte[] getHash(HashAlgorithm hashAlgo) {
|
||||
byte[] b = hashes.get(hashAlgo);
|
||||
if (b == null) {
|
||||
return new byte[0];
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
/** filename getter.
|
||||
* @return filename
|
||||
*/
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue