Removed subject line from wire format, added content type.

This commit is contained in:
akwizgran
2013-03-02 04:45:02 +00:00
parent 882420ebc2
commit a651e8ef73
24 changed files with 239 additions and 149 deletions

View File

@@ -472,7 +472,7 @@ DatabaseCleaner.Callback {
if(m.getGroup() != null) throw new IllegalArgumentException();
if(m.getAuthor() != null) throw new IllegalArgumentException();
if(!db.addPrivateMessage(txn, m, c)) return false;
db.addStatus(txn, c, m.getId(), !incoming);
db.addStatus(txn, c, m.getId(), incoming);
// Count the bytes stored
synchronized(spaceLock) {
bytesStoredSinceLastCheck += m.getSerialised().length;

View File

@@ -125,6 +125,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " authorId HASH," // Null for private/anon messages
+ " authorName VARCHAR," // Null for private/anon messages
+ " authorKey VARCHAR," // Null for private/anon messages
+ " contentType VARCHAR NOT NULL,"
+ " subject VARCHAR NOT NULL,"
+ " timestamp BIGINT NOT NULL,"
+ " length INT NOT NULL,"
@@ -633,10 +634,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO messages (messageId, parentId, groupId,"
+ " authorId, authorName, authorKey, subject, timestamp,"
+ " length, bodyStart, bodyLength, raw, sendability, read,"
+ " starred)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ZERO(),"
+ " authorId, authorName, authorKey, contentType, subject,"
+ " timestamp, length, bodyStart, bodyLength, raw,"
+ " sendability, read, starred)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ZERO(),"
+ " FALSE, FALSE)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getId().getBytes());
@@ -653,13 +654,14 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setString(5, a.getName());
ps.setBytes(6, a.getPublicKey());
}
ps.setString(7, m.getSubject());
ps.setLong(8, m.getTimestamp());
ps.setString(7, m.getContentType());
ps.setString(8, m.getSubject());
ps.setLong(9, m.getTimestamp());
byte[] raw = m.getSerialised();
ps.setInt(9, raw.length);
ps.setInt(10, m.getBodyStart());
ps.setInt(11, m.getBodyLength());
ps.setBytes(12, raw);
ps.setInt(10, raw.length);
ps.setInt(11, m.getBodyStart());
ps.setInt(12, m.getBodyLength());
ps.setBytes(13, raw);
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
@@ -707,22 +709,23 @@ abstract class JdbcDatabase implements Database<Connection> {
if(containsMessage(txn, m.getId())) return false;
PreparedStatement ps = null;
try {
String sql = "INSERT INTO messages (messageId, parentId, subject,"
+ " timestamp, length, bodyStart, bodyLength, raw,"
+ " contactId, read, starred)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, FALSE, FALSE)";
String sql = "INSERT INTO messages (messageId, parentId,"
+ " contentType, subject, timestamp, length, bodyStart,"
+ " bodyLength, raw, contactId, read, starred)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, FALSE, FALSE)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getId().getBytes());
if(m.getParent() == null) ps.setNull(2, BINARY);
else ps.setBytes(2, m.getParent().getBytes());
ps.setString(3, m.getSubject());
ps.setLong(4, m.getTimestamp());
ps.setString(3, m.getContentType());
ps.setString(4, m.getSubject());
ps.setLong(5, m.getTimestamp());
byte[] raw = m.getSerialised();
ps.setInt(5, raw.length);
ps.setInt(6, m.getBodyStart());
ps.setInt(7, m.getBodyLength());
ps.setBytes(8, raw);
ps.setInt(9, c.getInt());
ps.setInt(6, raw.length);
ps.setInt(7, m.getBodyStart());
ps.setInt(8, m.getBodyLength());
ps.setBytes(9, raw);
ps.setInt(10, c.getInt());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
@@ -1226,7 +1229,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null;
try {
String sql = "SELECT messageId, parentId, authorId, authorName,"
+ " authorKey, subject, timestamp, read, starred"
+ " authorKey, contentType, subject, timestamp, read,"
+ " starred"
+ " FROM messages"
+ " WHERE groupId = ?";
ps = txn.prepareStatement(sql);
@@ -1246,12 +1250,13 @@ abstract class JdbcDatabase implements Database<Connection> {
byte[] authorKey = rs.getBytes(5);
author = new Author(authorId, authorName, authorKey);
}
String subject = rs.getString(6);
long timestamp = rs.getLong(7);
boolean read = rs.getBoolean(8);
boolean starred = rs.getBoolean(9);
headers.add(new GroupMessageHeader(id, parent, subject,
timestamp, read, starred, g, author));
String contentType = rs.getString(6);
String subject = rs.getString(7);
long timestamp = rs.getLong(8);
boolean read = rs.getBoolean(9);
boolean starred = rs.getBoolean(10);
headers.add(new GroupMessageHeader(id, parent, contentType,
subject, timestamp, read, starred, g, author));
}
rs.close();
ps.close();
@@ -1268,8 +1273,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT m.messageId, parentId, subject, timestamp,"
+ " m.contactId, read, starred, seen"
String sql = "SELECT m.messageId, parentId, contentType, subject,"
+ " timestamp, m.contactId, read, starred, seen"
+ " FROM messages AS m"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
@@ -1283,14 +1288,15 @@ abstract class JdbcDatabase implements Database<Connection> {
MessageId id = new MessageId(rs.getBytes(1));
byte[] b = rs.getBytes(2);
MessageId parent = b == null ? null : new MessageId(b);
String subject = rs.getString(3);
long timestamp = rs.getLong(4);
ContactId contactId = new ContactId(rs.getInt(5));
boolean read = rs.getBoolean(6);
boolean starred = rs.getBoolean(7);
boolean seen = rs.getBoolean(8);
headers.add(new PrivateMessageHeader(id, parent, subject,
timestamp, read, starred, contactId, !seen));
String contentType = rs.getString(3);
String subject = rs.getString(4);
long timestamp = rs.getLong(5);
ContactId contactId = new ContactId(rs.getInt(6));
boolean read = rs.getBoolean(7);
boolean starred = rs.getBoolean(8);
boolean seen = rs.getBoolean(9);
headers.add(new PrivateMessageHeader(id, parent, contentType,
subject, timestamp, read, starred, contactId, seen));
}
rs.close();
ps.close();
@@ -1307,8 +1313,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT m.messageId, parentId, subject, timestamp,"
+ " read, starred, seen"
String sql = "SELECT m.messageId, parentId, contentType, subject,"
+ " timestamp, read, starred, seen"
+ " FROM messages AS m"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
@@ -1323,13 +1329,14 @@ abstract class JdbcDatabase implements Database<Connection> {
MessageId id = new MessageId(rs.getBytes(1));
byte[] b = rs.getBytes(2);
MessageId parent = b == null ? null : new MessageId(b);
String subject = rs.getString(3);
long timestamp = rs.getLong(4);
boolean read = rs.getBoolean(5);
boolean starred = rs.getBoolean(6);
boolean seen = rs.getBoolean(7);
headers.add(new PrivateMessageHeader(id, parent, subject,
timestamp, read, starred, c, !seen));
String contentType = rs.getString(3);
String subject = rs.getString(4);
long timestamp = rs.getLong(5);
boolean read = rs.getBoolean(6);
boolean starred = rs.getBoolean(7);
boolean seen = rs.getBoolean(8);
headers.add(new PrivateMessageHeader(id, parent, contentType,
subject, timestamp, read, starred, c, seen));
}
rs.close();
ps.close();

View File

@@ -1,6 +1,7 @@
package net.sf.briar.messaging;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_BODY_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SIGNATURE_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBJECT_LENGTH;
@@ -11,6 +12,9 @@ import static net.sf.briar.api.messaging.Types.MESSAGE;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.SecureRandom;
@@ -40,6 +44,7 @@ class MessageFactoryImpl implements MessageFactory {
private final MessageDigest messageDigest;
private final WriterFactory writerFactory;
private final Clock clock;
private final CharsetDecoder decoder;
@Inject
MessageFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory,
@@ -50,44 +55,46 @@ class MessageFactoryImpl implements MessageFactory {
messageDigest = crypto.getMessageDigest();
this.writerFactory = writerFactory;
this.clock = clock;
decoder = Charset.forName("UTF-8").newDecoder();
}
public Message createPrivateMessage(MessageId parent, String subject,
public Message createPrivateMessage(MessageId parent, String contentType,
byte[] body) throws IOException, GeneralSecurityException {
return createMessage(parent, null, null, null, null, subject, body);
return createMessage(parent, null, null, null, null, contentType, body);
}
public Message createAnonymousGroupMessage(MessageId parent, Group group,
String subject, byte[] body) throws IOException,
String contentType, byte[] body) throws IOException,
GeneralSecurityException {
return createMessage(parent, group, null, null, null, subject, body);
return createMessage(parent, group, null, null, null, contentType,
body);
}
public Message createAnonymousGroupMessage(MessageId parent, Group group,
PrivateKey groupKey, String subject, byte[] body)
PrivateKey groupKey, String contentType, byte[] body)
throws IOException, GeneralSecurityException {
return createMessage(parent, group, groupKey, null, null, subject,
return createMessage(parent, group, groupKey, null, null, contentType,
body);
}
public Message createPseudonymousMessage(MessageId parent, Group group,
Author author, PrivateKey authorKey, String subject, byte[] body)
Author author, PrivateKey authorKey, String contentType, byte[] body)
throws IOException, GeneralSecurityException {
return createMessage(parent, group, null, author, authorKey, subject,
body);
return createMessage(parent, group, null, author, authorKey,
contentType, body);
}
public Message createPseudonymousMessage(MessageId parent, Group group,
PrivateKey groupKey, Author author, PrivateKey authorKey,
String subject, byte[] body) throws IOException,
String contentType, byte[] body) throws IOException,
GeneralSecurityException {
return createMessage(parent, group, groupKey, author, authorKey,
subject, body);
contentType, body);
}
private Message createMessage(MessageId parent, Group group,
PrivateKey groupKey, Author author, PrivateKey authorKey,
String subject, byte[] body) throws IOException,
String contentType, byte[] body) throws IOException,
GeneralSecurityException {
// Validate the arguments
if((author == null) != (authorKey == null))
@@ -95,7 +102,7 @@ class MessageFactoryImpl implements MessageFactory {
if((group == null || group.getPublicKey() == null)
!= (groupKey == null))
throw new IllegalArgumentException();
if(subject.getBytes("UTF-8").length > MAX_SUBJECT_LENGTH)
if(contentType.getBytes("UTF-8").length > MAX_CONTENT_TYPE_LENGTH)
throw new IllegalArgumentException();
if(body.length > MAX_BODY_LENGTH)
throw new IllegalArgumentException();
@@ -127,7 +134,7 @@ class MessageFactoryImpl implements MessageFactory {
else writeGroup(w, group);
if(author == null) w.writeNull();
else writeAuthor(w, author);
w.writeString(subject);
w.writeString(contentType);
long timestamp = clock.currentTimeMillis();
w.writeInt64(timestamp);
byte[] salt = new byte[SALT_LENGTH];
@@ -158,7 +165,17 @@ class MessageFactoryImpl implements MessageFactory {
// Hash the message, including the signatures, to get the message ID
w.removeConsumer(digestingConsumer);
MessageId id = new MessageId(messageDigest.digest());
return new MessageImpl(id, parent, group, author, subject,
// If the content type is text/plain, extract a subject line
String subject;
if(contentType.equals("text/plain")) {
byte[] start = new byte[Math.min(MAX_SUBJECT_LENGTH, body.length)];
System.arraycopy(body, 0, start, 0, start.length);
decoder.reset();
subject = decoder.decode(ByteBuffer.wrap(start)).toString();
} else {
subject = "";
}
return new MessageImpl(id, parent, group, author, contentType, subject,
timestamp, out.toByteArray(), bodyStart, body.length);
}

View File

@@ -12,14 +12,14 @@ class MessageImpl implements Message {
private final MessageId id, parent;
private final Group group;
private final Author author;
private final String subject;
private final String contentType, subject;
private final long timestamp;
private final byte[] raw;
private final int bodyStart, bodyLength;
public MessageImpl(MessageId id, MessageId parent, Group group,
Author author, String subject, long timestamp, byte[] raw,
int bodyStart, int bodyLength) {
Author author, String contentType, String subject, long timestamp,
byte[] raw, int bodyStart, int bodyLength) {
if(bodyStart + bodyLength > raw.length)
throw new IllegalArgumentException();
if(bodyLength > MAX_BODY_LENGTH)
@@ -28,6 +28,7 @@ class MessageImpl implements Message {
this.parent = parent;
this.group = group;
this.author = author;
this.contentType = contentType;
this.subject = subject;
this.timestamp = timestamp;
this.raw = raw;
@@ -51,6 +52,10 @@ class MessageImpl implements Message {
return author;
}
public String getContentType() {
return contentType;
}
public String getSubject() {
return subject;
}

View File

@@ -1,6 +1,7 @@
package net.sf.briar.messaging;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_BODY_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SIGNATURE_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBJECT_LENGTH;
@@ -8,6 +9,9 @@ import static net.sf.briar.api.messaging.MessagingConstants.SALT_LENGTH;
import static net.sf.briar.api.messaging.Types.MESSAGE;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.messaging.Author;
@@ -24,11 +28,13 @@ class MessageReader implements StructReader<UnverifiedMessage> {
private final StructReader<Group> groupReader;
private final StructReader<Author> authorReader;
private final CharsetDecoder decoder;
MessageReader(StructReader<Group> groupReader,
StructReader<Author> authorReader) {
this.groupReader = groupReader;
this.authorReader = authorReader;
decoder = Charset.forName("UTF-8").newDecoder();
}
public UnverifiedMessage readStruct(Reader r) throws IOException {
@@ -55,8 +61,8 @@ class MessageReader implements StructReader<UnverifiedMessage> {
Author author = null;
if(r.hasNull()) r.readNull();
else author = authorReader.readStruct(r);
// Read the subject
String subject = r.readString(MAX_SUBJECT_LENGTH);
// Read the content type
String contentType = r.readString(MAX_CONTENT_TYPE_LENGTH);
// Read the timestamp
long timestamp = r.readInt64();
if(timestamp < 0) throw new FormatException();
@@ -65,6 +71,16 @@ class MessageReader implements StructReader<UnverifiedMessage> {
if(salt.length < SALT_LENGTH) throw new FormatException();
// Read the message body
byte[] body = r.readBytes(MAX_BODY_LENGTH);
// If the content type is text/plain, extract a subject line
String subject;
if(contentType.equals("text/plain")) {
byte[] start = new byte[Math.min(MAX_SUBJECT_LENGTH, body.length)];
System.arraycopy(body, 0, start, 0, start.length);
decoder.reset();
subject = decoder.decode(ByteBuffer.wrap(start)).toString();
} else {
subject = "";
}
// Record the offset of the body within the message
int bodyStart = (int) counting.getCount() - body.length;
// Record the length of the data covered by the author's signature
@@ -83,8 +99,8 @@ class MessageReader implements StructReader<UnverifiedMessage> {
r.removeConsumer(counting);
r.removeConsumer(copying);
byte[] raw = copying.getCopy();
return new UnverifiedMessage(parent, group, author, subject, timestamp,
raw, authorSig, groupSig, bodyStart, body.length,
signedByAuthor, signedByGroup);
return new UnverifiedMessage(parent, group, author, contentType,
subject, timestamp, raw, authorSig, groupSig, bodyStart,
body.length, signedByAuthor, signedByGroup);
}
}

View File

@@ -53,7 +53,8 @@ class MessageVerifierImpl implements MessageVerifier {
if(!signature.verify(m.getGroupSignature()))
throw new GeneralSecurityException();
}
return new MessageImpl(id, m.getParent(), group, author, m.getSubject(),
m.getTimestamp(), raw, m.getBodyStart(), m.getBodyLength());
return new MessageImpl(id, m.getParent(), group, author,
m.getContentType(), m.getSubject(), m.getTimestamp(), raw,
m.getBodyStart(), m.getBodyLength());
}
}