package uk.ac.cam.db538.cryptosms.storage;
import java.io.IOException;
import org.joda.time.DateTime;
import org.joda.time.DateTimeComparator;
import org.joda.time.format.ISODateTimeFormat;
import uk.ac.cam.db538.cryptosms.CustomAsserts;
import uk.ac.cam.db538.cryptosms.crypto.Encryption;
import uk.ac.cam.db538.cryptosms.crypto.EncryptionNone;
import uk.ac.cam.db538.cryptosms.crypto.EncryptionInterface.EncryptionException;
import uk.ac.cam.db538.cryptosms.storage.Conversation;
import uk.ac.cam.db538.cryptosms.storage.MessageData;
import uk.ac.cam.db538.cryptosms.storage.Storage;
import uk.ac.cam.db538.cryptosms.storage.StorageFileException;
import uk.ac.cam.db538.cryptosms.storage.MessageData.MessageType;
import uk.ac.cam.db538.cryptosms.utils.Charset;
import uk.ac.cam.db538.cryptosms.utils.LowLevel;
import junit.framework.TestCase;
public class MessageData_Test extends TestCase {
protected void setUp() throws Exception {
super.setUp();
EncryptionNone.initEncryption();
Common.clearStorageFile();
}
protected void tearDown() throws Exception {
super.tearDown();
Common.closeStorageFile();
}
private boolean deliveredPart = true;
private boolean deliveredAll = true;
private MessageType messageType = MessageType.OUTGOING;
private boolean unread = true;
private boolean compressed = true;
private boolean ascii = true;
private DateTime timeStamp = new DateTime();
private String messageBody = "Testing body";
private byte[] messageBodyData = messageBody.getBytes();
private short messageBodyLength = (short) messageBodyData.length;
private long indexParent = 127L;
private long indexMessageParts = 120L;
private long indexPrev = 225L;
private long indexNext = 12L;
private void setData(MessageData msg) {
msg.setDeliveredPart(deliveredPart);
msg.setDeliveredAll(deliveredAll);
msg.setMessageType(messageType);
msg.setUnread(unread);
msg.setCompressed(compressed);
msg.setAscii(ascii);
msg.setTimeStamp(timeStamp);
msg.setMessageBody(messageBodyData);
msg.setIndexParent(indexParent);
msg.setIndexMessageParts(indexMessageParts);
msg.setIndexPrev(indexPrev);
msg.setIndexNext(indexNext);
}
private void checkData(MessageData msg) {
assertEquals(msg.getDeliveredPart(), deliveredPart);
assertEquals(msg.getDeliveredAll(), deliveredAll);
assertEquals(msg.getMessageType(), messageType);
assertEquals(msg.getUnread(), unread);
assertEquals(msg.getCompressed(), compressed);
assertEquals(msg.getAscii(), ascii);
assertEquals(DateTimeComparator.getInstance().compare(msg.getTimeStamp(), timeStamp), 0);
assertEquals(msg.getMessageBody().length, messageBodyLength);
CustomAsserts.assertArrayEquals(msg.getMessageBody(), messageBodyData);
assertEquals(msg.getIndexParent(), indexParent);
assertEquals(msg.getIndexMessageParts(), indexMessageParts);
assertEquals(msg.getIndexPrev(), indexPrev);
assertEquals(msg.getIndexNext(), indexNext);
}
public void testConstruction() throws StorageFileException, IOException {
// create a Message entry
Conversation conv = Conversation.createConversation();
MessageData msg = MessageData.createMessageData(conv);
// check structure
assertTrue(Common.checkStructure());
setData(msg);
msg.saveToFile();
long index = msg.getEntryIndex();
// for it to be re-read
MessageData.forceClearCache();
msg = MessageData.getMessageData(index);
checkData(msg);
}
public void testIndices() throws StorageFileException, IOException {
// INDICES OUT OF BOUNDS
Conversation conv = Conversation.createConversation();
MessageData msg = MessageData.createMessageData(conv);
// indexMessageParts
try {
msg.setIndexMessageParts(0x0100000000L);
assertTrue(false);
} catch (IndexOutOfBoundsException ex) {
}
try {
msg.setIndexMessageParts(-1L);
assertTrue(false);
} catch (IndexOutOfBoundsException ex) {
}
// indexPrev
try {
msg.setIndexPrev(0x0100000000L);
assertTrue(false);
} catch (IndexOutOfBoundsException ex) {
}
try {
msg.setIndexPrev(-1L);
assertTrue(false);
} catch (IndexOutOfBoundsException ex) {
}
// indexNext
try {
msg.setIndexNext(0x0100000000L);
assertTrue(false);
} catch (IndexOutOfBoundsException ex) {
}
try {
msg.setIndexNext(-1L);
assertTrue(false);
} catch (IndexOutOfBoundsException ex) {
}
}
public void testSetNumberOfParts() throws StorageFileException, IOException {
// set data
Conversation conv = Conversation.createConversation();
MessageData msg = MessageData.createMessageData(conv);
long index = msg.getEntryIndex();
assertTrue(Common.checkStructure());
msg = MessageData.getMessageData(index); // because checkStructure clears cache
msg.setNumberOfParts(5);
assertTrue(Common.checkStructure());
assertNotNull(msg.getPartData(4));
try {
msg.getPartData(5);
fail("Should not reach here!");
} catch (IndexOutOfBoundsException e) {
}
msg = MessageData.getMessageData(index);
msg.setNumberOfParts(7);
assertTrue(Common.checkStructure());
assertNotNull(msg.getPartData(2));
assertNotNull(msg.getPartData(3));
assertNotNull(msg.getPartData(4));
assertNotNull(msg.getPartData(6));
try {
msg.getPartData(7);
fail("Should not reach here!");
} catch (IndexOutOfBoundsException e) {
}
msg = MessageData.getMessageData(index);
msg.setNumberOfParts(4);
assertTrue(Common.checkStructure());
assertNotNull(msg.getPartData(0));
assertNotNull(msg.getPartData(1));
assertNotNull(msg.getPartData(2));
assertNotNull(msg.getPartData(3));
try {
msg.getPartData(4);
fail("Should not reach here!");
} catch (IndexOutOfBoundsException e) {
}
msg = MessageData.getMessageData(index);
msg.setNumberOfParts(1);
assertTrue(Common.checkStructure());
try {
msg.getPartData(1);
fail("Should not reach here!");
} catch (IndexOutOfBoundsException e) {
}
}
public void testSettingGettingPartData() throws StorageFileException, IOException {
Conversation conv = Conversation.createConversation();
MessageData msg = MessageData.createMessageData(conv);
msg.setNumberOfParts(3);
// indices out of bounds
try {
msg.getPartData(-1);
fail("Should not reach here");
} catch (IndexOutOfBoundsException e) {
}
try {
msg.getPartData(3);
fail("Should not reach here");
} catch (IndexOutOfBoundsException e) {
}
try {
msg.setPartData(-1, new byte[4]);
fail("Should not reach here");
} catch (IndexOutOfBoundsException e) {
}
try {
msg.setPartData(3, new byte[4]);
fail("Should not reach here");
} catch (IndexOutOfBoundsException e) {
}
try {
msg.getPartDelivered(-1);
fail("Should not reach here");
} catch (IndexOutOfBoundsException e) {
}
try {
msg.getPartDelivered(3);
fail("Should not reach here");
} catch (IndexOutOfBoundsException e) {
}
try {
msg.setPartDelivered(-1, false);
fail("Should not reach here");
} catch (IndexOutOfBoundsException e) {
}
try {
msg.setPartDelivered(3, false);
fail("Should not reach here");
} catch (IndexOutOfBoundsException e) {
}
// setting/getting
byte[] data = Encryption.getEncryption().generateRandomData(280);
byte[] dataCut = LowLevel.cutData(data, 0, 133);
msg.setPartData(2, data);
assertEquals(0, msg.getPartData(0).length);
assertEquals(0, msg.getPartData(1).length);
CustomAsserts.assertArrayEquals(msg.getPartData(2), dataCut);
msg.setPartData(0, data);
CustomAsserts.assertArrayEquals(msg.getPartData(0), dataCut);
assertEquals(0, msg.getPartData(1).length);
CustomAsserts.assertArrayEquals(msg.getPartData(2), dataCut);
msg.setPartDelivered(1, true);
assertEquals(false, msg.getPartDelivered(0));
assertEquals(true, msg.getPartDelivered(1));
assertEquals(false, msg.getPartDelivered(2));
msg.setPartDelivered(0, true);
assertEquals(true, msg.getPartDelivered(0));
assertEquals(true, msg.getPartDelivered(1));
assertEquals(false, msg.getPartDelivered(2));
msg.setPartDelivered(1, false);
assertEquals(true, msg.getPartDelivered(0));
assertEquals(false, msg.getPartDelivered(1));
assertEquals(false, msg.getPartDelivered(2));
}
public void testCreateData() throws StorageFileException, IOException, EncryptionException {
// set data
Conversation conv = Conversation.createConversation();
MessageData msg = MessageData.createMessageData(conv);
setData(msg);
msg.saveToFile();
// compute expected values
byte flags = 0;
flags |= (deliveredPart) ? 0x80 : 0x00;
flags |= (deliveredAll) ? 0x40 : 0x00;
flags |= (messageType == MessageType.OUTGOING) ? 0x20 : 0x00;
flags |= (unread) ? 0x10 : 0x00;
flags |= (compressed) ? 0x08 : 0x00;
flags |= (ascii) ? 0x04: 0x00;
// get the generated data
byte[] dataEncrypted = Storage.getStorage().getEntry(msg.getEntryIndex());
// chunk length
assertEquals(dataEncrypted.length, Storage.CHUNK_SIZE);
// decrypt the encoded part
byte[] dataPlain = Encryption.getEncryption().decryptSymmetricWithMasterKey(dataEncrypted);
// check the data
assertEquals(dataPlain[0], flags);
DateTime time = ISODateTimeFormat.dateTimeParser().parseDateTime(Charset.fromAscii8(dataPlain, 1, 29));
assertEquals(DateTimeComparator.getInstance().compare(time, timeStamp), 0);
assertEquals(LowLevel.getUnsignedShort(dataPlain, 30), messageBodyLength);
CustomAsserts.assertArrayEquals(LowLevel.cutData(dataPlain, 32, messageBodyLength), messageBodyData);
assertEquals(LowLevel.getUnsignedInt(dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 16), indexParent);
assertEquals(LowLevel.getUnsignedInt(dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 12), indexMessageParts);
assertEquals(LowLevel.getUnsignedInt(dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 8), indexPrev);
assertEquals(LowLevel.getUnsignedInt(dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 4), indexNext);
}
public void testParseData() throws StorageFileException, IOException, EncryptionException {
Conversation conv = Conversation.createConversation();
MessageData msg = MessageData.createMessageData(conv);
long index = msg.getEntryIndex();
// prepare stuff
byte flags = 0;
flags |= (deliveredPart) ? 0x80 : 0x00;
flags |= (deliveredAll) ? 0x40 : 0x00;
flags |= (messageType == MessageType.OUTGOING) ? 0x20 : 0x00;
flags |= (unread) ? 0x10 : 0x00;
flags |= (compressed) ? 0x08 : 0x00;
flags |= (ascii) ? 0x04: 0x00;
// create plain data
byte[] dataPlain = new byte[Storage.ENCRYPTED_ENTRY_SIZE];
dataPlain[0] = flags;
System.arraycopy(Charset.toAscii8(ISODateTimeFormat.dateTime().print(timeStamp), 29), 0, dataPlain, 1, 29);
System.arraycopy(LowLevel.getBytesUnsignedShort(messageBodyLength), 0, dataPlain, 30, 2);
System.arraycopy(LowLevel.wrapData(messageBodyData, 140), 0, dataPlain, 32, 140);
System.arraycopy(LowLevel.getBytesUnsignedInt(indexParent), 0, dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 16, 4);
System.arraycopy(LowLevel.getBytesUnsignedInt(indexMessageParts), 0, dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 12, 4);
System.arraycopy(LowLevel.getBytesUnsignedInt(indexPrev), 0, dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 8, 4);
System.arraycopy(LowLevel.getBytesUnsignedInt(indexNext), 0, dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 4, 4);
// encrypt it
byte[] dataEncrypted = Encryption.getEncryption().encryptSymmetricWithMasterKey(dataPlain);
// inject it in the file
Storage.getStorage().setEntry(index, dataEncrypted);
// have it parsed
MessageData.forceClearCache();
msg = MessageData.getMessageData(index);
// check the indices
assertEquals(deliveredPart, msg.getDeliveredPart());
assertEquals(deliveredAll, msg.getDeliveredAll());
assertEquals(messageType, msg.getMessageType());
assertEquals(unread, msg.getUnread());
assertEquals(compressed, msg.getCompressed());
assertEquals(ascii, msg.getAscii());
assertEquals(DateTimeComparator.getInstance().compare(timeStamp, msg.getTimeStamp()), 0);
assertEquals(messageBodyLength, msg.getMessageBody().length);
CustomAsserts.assertArrayEquals(messageBodyData, msg.getMessageBody());
assertEquals(indexParent, msg.getIndexParent());
assertEquals(indexMessageParts, msg.getIndexMessageParts());
assertEquals(indexPrev, msg.getIndexPrev());
assertEquals(indexNext, msg.getIndexNext());
}
}