package net.i2p.data;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
/**
* Defines the actual payload of a message being delivered, including the
* standard encryption wrapping, as defined by the I2P data structure spec.
*
* This is used mostly in I2CP, where we used to do end-to-end encryption.
* Since we don't any more, you probably just want to use the
* get/set EncryptedData methods.
*
* @author jrandom
*/
public class Payload extends DataStructureImpl {
//private final static Log _log = new Log(Payload.class);
private byte[] _encryptedData;
private byte[] _unencryptedData;
/** So we don't OOM on I2CP protocol errors. Actual max is smaller. */
private static final int MAX_LENGTH = 64*1024;
public Payload() {
}
/**
* Retrieve the unencrypted body of the message.
*
* Deprecated.
* Unless you are doing encryption, use getEncryptedData() instead.
*
* @return body of the message, or null if the message has either not been
* decrypted yet or if the hash is not correct
*/
public byte[] getUnencryptedData() {
return _unencryptedData;
}
/**
* Populate the message body with data. This does not automatically encrypt
* yet.
*
* Deprecated.
* Unless you are doing encryption, use setEncryptedData() instead.
* @throws IllegalArgumentException if bigger than 64KB
*/
public void setUnencryptedData(byte[] data) {
if (data.length > MAX_LENGTH)
throw new IllegalArgumentException();
_unencryptedData = data;
}
/** the real data */
public byte[] getEncryptedData() {
return _encryptedData;
}
/**
* the real data
* @throws IllegalArgumentException if bigger than 64KB
*/
public void setEncryptedData(byte[] data) {
if (data.length > MAX_LENGTH)
throw new IllegalArgumentException();
_encryptedData = data;
}
public int getSize() {
if (_unencryptedData != null)
return _unencryptedData.length;
else if (_encryptedData != null)
return _encryptedData.length;
else
return 0;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
int size = (int) DataHelper.readLong(in, 4);
if (size < 0 || size > MAX_LENGTH) throw new DataFormatException("payload size out of range (" + size + ")");
_encryptedData = new byte[size];
int read = read(in, _encryptedData);
if (read != size) throw new DataFormatException("Incorrect number of bytes read in the payload structure");
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("read payload: " + read + " bytes");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_encryptedData == null) throw new DataFormatException("Not yet encrypted. Please set the encrypted data");
DataHelper.writeLong(out, 4, _encryptedData.length);
out.write(_encryptedData);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("wrote payload: " + _encryptedData.length);
}
/**
* @return the written length (NOT the new offset)
*/
public int writeBytes(byte target[], int offset) {
if (_encryptedData == null) throw new IllegalStateException("Not yet encrypted. Please set the encrypted data");
DataHelper.toLong(target, offset, 4, _encryptedData.length);
offset += 4;
System.arraycopy(_encryptedData, 0, target, offset, _encryptedData.length);
return 4 + _encryptedData.length;
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if ((object == null) || !(object instanceof Payload)) return false;
Payload p = (Payload) object;
return Arrays.equals(_unencryptedData, p.getUnencryptedData())
&& Arrays.equals(_encryptedData, p.getEncryptedData());
}
@Override
public int hashCode() {
return DataHelper.hashCode(_encryptedData != null ? _encryptedData : _unencryptedData);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(32);
buf.append("[Payload: ");
if (_encryptedData != null)
buf.append(_encryptedData.length).append(" bytes");
else
buf.append("null");
buf.append("]");
return buf.toString();
}
}