import javax.crypto.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.math.BigInteger;
public class Data
{
//the data we send over the network consists of:
//the type of data as one byte
byte typeData;
//the length of data as two bytes
short lengthData;
//the actual raw data
byte[] currentData;
//if the data is a string
//generate the correct bytes
Data(String inputData)
{
try
{
currentData = inputData.getBytes("US-ASCII");
}
catch(UnsupportedEncodingException e)
{
currentData = inputData.getBytes();
}
typeData = 0;
lengthData = (short)currentData.length;
}
//if the data is a binary array
//generate the correct bytes
Data(byte[] inputData)
{
currentData = inputData;
typeData = 1;
lengthData = (short)currentData.length;
}
//if the data is an int
//generate the correct bytes
Data(int inputData)
{
currentData = String.valueOf(inputData).getBytes();
typeData = 2;
lengthData = (short)currentData.length;
}
//if the data is an big int
//treat it as an int
Data(BigInteger inputData)
{
currentData = inputData.toString().getBytes();
typeData = 2;
lengthData = (short)currentData.length;
}
//if the data is a structured data
//generate the correct bytes
Data(ArrayList<Data> inputDataList, Cipher encrypter)
{
byte[] currentBuffer = new byte[0];
ByteBuffer tmpBuffer;
for(int i = 0; i < inputDataList.size(); i++)
{
Data inputData = inputDataList.get(i);
tmpBuffer = ByteBuffer.allocate(inputData.getLength() + 3);
tmpBuffer.order(ByteOrder.LITTLE_ENDIAN);
tmpBuffer.put(inputData.getType());
tmpBuffer.putShort(inputData.getLength());
tmpBuffer.put(inputData.getData());
currentBuffer = concatByteArray(currentBuffer, tmpBuffer.array());
}
currentData = currentBuffer;
//if you have passed on a cipher then encrypt the data
//and define it as binary data
if(encrypter != null)
{
try
{
currentData = encrypter.doFinal(currentData);
} catch(Exception e) {
System.out.println("Problems happened with the encrypter");
e.printStackTrace();
}
typeData = 1;
lengthData = (short)currentData.length;
}
else
{
typeData = 3;
lengthData = (short)currentData.length;
}
}
//read in a raw data object
//nothing needs to be generated
Data(byte inputType, short inputLength, byte[] inputData)
{
typeData = inputType;
lengthData = inputLength;
currentData = inputData;
}
//define get methods for all of the variables
byte getType()
{
return typeData;
}
short getLength()
{
return lengthData;
}
byte getData(short i)
{
return currentData[i];
}
byte[] getData()
{
return currentData;
}
//get the internal data for a structured data object
ArrayList<Data> unnest(Cipher decrypter)
{
ArrayList<Data> currentDataList = new ArrayList<Data>();
try
{
byte[] currentDataArray = new byte[0];
byte[] tmpDataArray;
byte newTypeData;
short newLengthData;
byte[] newData;
//if its encrypted, then unencrypt
if((decrypter != null)&&(typeData == 1))
{
currentDataArray = decrypter.doFinal(currentData);
}
else if(typeData == 3)
{
currentDataArray = currentData;
}
//keep extracting data objects from the array while they exist
while(currentDataArray.length >= 3)
{
newTypeData = currentDataArray[0];
newLengthData = (short) (currentDataArray[1] + (currentDataArray[2] * 256));
newData = new byte[newLengthData];
System.arraycopy(currentDataArray, 3, newData, 0, newLengthData);
tmpDataArray = new byte[currentDataArray.length - (newLengthData + 3)];
System.arraycopy(currentDataArray, (newLengthData + 3), tmpDataArray, 0, tmpDataArray.length);
currentDataArray = tmpDataArray;
currentDataList.add(new Data(newTypeData, newLengthData, newData));
}
}
catch(Exception e)
{
}
return currentDataList;
}
//joins two byte arrays together
byte[] concatByteArray(byte[] inputArray1, byte[] inputArray2)
{
byte[] currentArray = new byte[inputArray1.length + inputArray2.length];
System.arraycopy(inputArray1, 0, currentArray, 0, inputArray1.length);
System.arraycopy(inputArray2, 0, currentArray, inputArray1.length, inputArray2.length);
return currentArray;
}
boolean equals(Data inputData)
{
if(typeData != inputData.getType())
{
return false;
}
if(lengthData != inputData.getLength())
{
return false;
}
if(!Arrays.equals(currentData, inputData.getData()))
{
return false;
}
return true;
}
public String toString()
{
String currentString = "";
currentString += "Type: " + typeData + "\n";
currentString += "Length: " + lengthData + "\n";
currentString += "Data: ";
for(int i = 0; i < currentData.length; i++)
{
if((typeData == 0)||(typeData == 2))
currentString += String.format("%c", currentData[i]);
else
currentString += String.format("%x", currentData[i]);
}
currentString += "\n";
return currentString;
}
//sends the data object out to the stream
void write(OutputStream serverOut) throws IOException
{
ByteBuffer currentBuffer = ByteBuffer.allocate(lengthData + 3);
currentBuffer.order(ByteOrder.LITTLE_ENDIAN);
currentBuffer.put(typeData);
currentBuffer.putShort(lengthData);
currentBuffer.put(currentData);
serverOut.write(currentBuffer.array());
serverOut.flush();
}
//gets a data object from the stream
static Data read(InputStream serverIn) throws IOException
{
byte tmpTypeData;
short tmpLengthData;
byte[] tmpData;
int inputTypeData = serverIn.read();
while(inputTypeData == -1)
inputTypeData = serverIn.read();
tmpTypeData = (byte)inputTypeData;
int inputLengthData1 = serverIn.read();
while(inputLengthData1 == -1)
inputLengthData1 = serverIn.read();
int inputLengthData2 = serverIn.read();
while(inputLengthData2 == -1)
inputLengthData2 = serverIn.read();
tmpLengthData = (short) (inputLengthData1 + (inputLengthData2 * 256));
int currentLength = tmpLengthData;
int currentAmountRead = 0;
tmpData = new byte[currentLength];
int result;
while(currentAmountRead != tmpLengthData)
{
result = serverIn.read(tmpData, currentAmountRead, currentLength);
if(currentAmountRead != -1)
{
currentAmountRead += result;
currentLength = tmpLengthData - currentAmountRead;
}
}
return new Data(tmpTypeData, tmpLengthData, tmpData);
}
}