mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
When replying to a message, don't use an earlier timestamp.
This produces a saner user experience when devices have differing clocks.
This commit is contained in:
@@ -244,7 +244,8 @@ NoContactsDialog.Listener {
|
|||||||
FragmentManager fm = getSupportFragmentManager();
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
noContactsDialog.show(fm, "NoContactsDialog");
|
noContactsDialog.show(fm, "NoContactsDialog");
|
||||||
} else {
|
} else {
|
||||||
startActivity(new Intent(this, WritePrivateMessageActivity.class));
|
startActivity(new Intent(this,
|
||||||
|
WritePrivateMessageActivity.class));
|
||||||
}
|
}
|
||||||
} else if(view == shareButton) {
|
} else if(view == shareButton) {
|
||||||
String apkPath = getPackageCodePath();
|
String apkPath = getPackageCodePath();
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ implements OnClickListener {
|
|||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
@Inject private volatile LifecycleManager lifecycleManager;
|
||||||
private volatile MessageId messageId = null;
|
private volatile MessageId messageId = null;
|
||||||
|
private volatile long timestamp = -1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
@@ -79,7 +80,7 @@ implements OnClickListener {
|
|||||||
messageId = new MessageId(b);
|
messageId = new MessageId(b);
|
||||||
String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
|
String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
|
||||||
if(contentType == null) throw new IllegalStateException();
|
if(contentType == null) throw new IllegalStateException();
|
||||||
long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
||||||
if(timestamp == -1) throw new IllegalStateException();
|
if(timestamp == -1) throw new IllegalStateException();
|
||||||
|
|
||||||
if(state == null) {
|
if(state == null) {
|
||||||
@@ -262,6 +263,7 @@ implements OnClickListener {
|
|||||||
Intent i = new Intent(this, WritePrivateMessageActivity.class);
|
Intent i = new Intent(this, WritePrivateMessageActivity.class);
|
||||||
i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
|
i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
|
||||||
i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
|
i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
|
||||||
|
i.putExtra("net.sf.briar.TIMESTAMP", timestamp);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
setResult(RESULT_REPLY);
|
setResult(RESULT_REPLY);
|
||||||
finish();
|
finish();
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
private volatile LocalAuthor localAuthor = null;
|
private volatile LocalAuthor localAuthor = null;
|
||||||
private volatile ContactId contactId = null;
|
private volatile ContactId contactId = null;
|
||||||
private volatile MessageId parentId = null;
|
private volatile MessageId parentId = null;
|
||||||
|
private volatile long timestamp = -1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
@@ -76,6 +77,7 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
if(id != -1) contactId = new ContactId(id);
|
if(id != -1) contactId = new ContactId(id);
|
||||||
byte[] b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
|
byte[] b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
|
||||||
if(b != null) parentId = new MessageId(b);
|
if(b != null) parentId = new MessageId(b);
|
||||||
|
timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
||||||
|
|
||||||
if(state != null) {
|
if(state != null) {
|
||||||
id = state.getInt("net.sf.briar.CONTACT_ID", -1);
|
id = state.getInt("net.sf.briar.CONTACT_ID", -1);
|
||||||
@@ -262,8 +264,11 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
lifecycleManager.waitForDatabase();
|
lifecycleManager.waitForDatabase();
|
||||||
|
// Don't use an earlier timestamp than the parent
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
time = Math.max(time, timestamp + 1);
|
||||||
Message m = messageFactory.createPrivateMessage(parentId,
|
Message m = messageFactory.createPrivateMessage(parentId,
|
||||||
"text/plain", body);
|
"text/plain", time, body);
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
db.addLocalPrivateMessage(m, contactId);
|
db.addLocalPrivateMessage(m, contactId);
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ implements OnClickListener {
|
|||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
@Inject private volatile LifecycleManager lifecycleManager;
|
||||||
private volatile MessageId messageId = null;
|
private volatile MessageId messageId = null;
|
||||||
|
private volatile long timestamp = -1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
@@ -78,7 +79,7 @@ implements OnClickListener {
|
|||||||
String authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
|
String authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
|
||||||
String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
|
String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
|
||||||
if(contentType == null) throw new IllegalStateException();
|
if(contentType == null) throw new IllegalStateException();
|
||||||
long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
||||||
if(timestamp == -1) throw new IllegalStateException();
|
if(timestamp == -1) throw new IllegalStateException();
|
||||||
|
|
||||||
if(state == null) {
|
if(state == null) {
|
||||||
@@ -266,6 +267,7 @@ implements OnClickListener {
|
|||||||
Intent i = new Intent(this, WriteGroupPostActivity.class);
|
Intent i = new Intent(this, WriteGroupPostActivity.class);
|
||||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
||||||
i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
|
i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
|
||||||
|
i.putExtra("net.sf.briar.TIMESTAMP", timestamp);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
setResult(RESULT_REPLY);
|
setResult(RESULT_REPLY);
|
||||||
finish();
|
finish();
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
private volatile LocalAuthor localAuthor = null;
|
private volatile LocalAuthor localAuthor = null;
|
||||||
private volatile Group group = null;
|
private volatile Group group = null;
|
||||||
private volatile MessageId parentId = null;
|
private volatile MessageId parentId = null;
|
||||||
|
private volatile long timestamp = -1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
@@ -84,6 +85,7 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
if(b != null) groupId = new GroupId(b);
|
if(b != null) groupId = new GroupId(b);
|
||||||
b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
|
b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
|
||||||
if(b != null) parentId = new MessageId(b);
|
if(b != null) parentId = new MessageId(b);
|
||||||
|
timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
||||||
|
|
||||||
if(state != null) {
|
if(state != null) {
|
||||||
b = state.getByteArray("net.sf.briar.LOCAL_AUTHOR_ID");
|
b = state.getByteArray("net.sf.briar.LOCAL_AUTHOR_ID");
|
||||||
@@ -318,15 +320,18 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
// FIXME: This should happen on a CryptoExecutor thread
|
// FIXME: This should happen on a CryptoExecutor thread
|
||||||
private Message createMessage(byte[] body) throws IOException,
|
private Message createMessage(byte[] body) throws IOException,
|
||||||
GeneralSecurityException {
|
GeneralSecurityException {
|
||||||
|
// Don't use an earlier timestamp than the parent
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
time = Math.max(time, timestamp + 1);
|
||||||
if(localAuthor == null) {
|
if(localAuthor == null) {
|
||||||
return messageFactory.createAnonymousMessage(parentId, group,
|
return messageFactory.createAnonymousMessage(parentId, group,
|
||||||
"text/plain", body);
|
"text/plain", time, body);
|
||||||
} else {
|
} else {
|
||||||
KeyParser keyParser = crypto.getSignatureKeyParser();
|
KeyParser keyParser = crypto.getSignatureKeyParser();
|
||||||
byte[] authorKeyBytes = localAuthor.getPrivateKey();
|
byte[] authorKeyBytes = localAuthor.getPrivateKey();
|
||||||
PrivateKey authorKey = keyParser.parsePrivateKey(authorKeyBytes);
|
PrivateKey authorKey = keyParser.parsePrivateKey(authorKeyBytes);
|
||||||
return messageFactory.createPseudonymousMessage(parentId,
|
return messageFactory.createPseudonymousMessage(parentId,
|
||||||
group, localAuthor, authorKey, "text/plain", body);
|
group, localAuthor, authorKey, "text/plain", time, body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,15 +10,17 @@ public interface MessageFactory {
|
|||||||
|
|
||||||
/** Creates a private message. */
|
/** Creates a private message. */
|
||||||
Message createPrivateMessage(MessageId parent, String contentType,
|
Message createPrivateMessage(MessageId parent, String contentType,
|
||||||
byte[] body) throws IOException, GeneralSecurityException;
|
long timestamp, byte[] body) throws IOException,
|
||||||
|
GeneralSecurityException;
|
||||||
|
|
||||||
/** Creates an anonymous group message. */
|
/** Creates an anonymous group message. */
|
||||||
Message createAnonymousMessage(MessageId parent, Group group,
|
Message createAnonymousMessage(MessageId parent, Group group,
|
||||||
String contentType, byte[] body) throws IOException,
|
String contentType, long timestamp, byte[] body) throws IOException,
|
||||||
GeneralSecurityException;
|
GeneralSecurityException;
|
||||||
|
|
||||||
/** Creates a pseudonymous group message. */
|
/** Creates a pseudonymous group message. */
|
||||||
Message createPseudonymousMessage(MessageId parent, Group group,
|
Message createPseudonymousMessage(MessageId parent, Group group,
|
||||||
Author author, PrivateKey privateKey, String contentType,
|
Author author, PrivateKey privateKey, String contentType,
|
||||||
byte[] body) throws IOException, GeneralSecurityException;
|
long timestamp, byte[] body) throws IOException,
|
||||||
|
GeneralSecurityException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import java.security.SecureRandom;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import net.sf.briar.api.Author;
|
import net.sf.briar.api.Author;
|
||||||
import net.sf.briar.api.clock.Clock;
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
import net.sf.briar.api.crypto.MessageDigest;
|
import net.sf.briar.api.crypto.MessageDigest;
|
||||||
import net.sf.briar.api.crypto.PrivateKey;
|
import net.sf.briar.api.crypto.PrivateKey;
|
||||||
@@ -39,39 +38,40 @@ class MessageFactoryImpl implements MessageFactory {
|
|||||||
private final SecureRandom random;
|
private final SecureRandom random;
|
||||||
private final MessageDigest messageDigest;
|
private final MessageDigest messageDigest;
|
||||||
private final WriterFactory writerFactory;
|
private final WriterFactory writerFactory;
|
||||||
private final Clock clock;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MessageFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory,
|
MessageFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory) {
|
||||||
Clock clock) {
|
|
||||||
signature = crypto.getSignature();
|
signature = crypto.getSignature();
|
||||||
random = crypto.getSecureRandom();
|
random = crypto.getSecureRandom();
|
||||||
messageDigest = crypto.getMessageDigest();
|
messageDigest = crypto.getMessageDigest();
|
||||||
this.writerFactory = writerFactory;
|
this.writerFactory = writerFactory;
|
||||||
this.clock = clock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message createPrivateMessage(MessageId parent, String contentType,
|
public Message createPrivateMessage(MessageId parent, String contentType,
|
||||||
byte[] body) throws IOException, GeneralSecurityException {
|
long timestamp, byte[] body) throws IOException,
|
||||||
return createMessage(parent, null, null, null, contentType, body);
|
GeneralSecurityException {
|
||||||
|
return createMessage(parent, null, null, null, contentType, timestamp,
|
||||||
|
body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message createAnonymousMessage(MessageId parent, Group group,
|
public Message createAnonymousMessage(MessageId parent, Group group,
|
||||||
String contentType, byte[] body) throws IOException,
|
String contentType, long timestamp, byte[] body) throws IOException,
|
||||||
GeneralSecurityException {
|
GeneralSecurityException {
|
||||||
return createMessage(parent, group, null, null, contentType, body);
|
return createMessage(parent, group, null, null, contentType, timestamp,
|
||||||
|
body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message createPseudonymousMessage(MessageId parent, Group group,
|
public Message createPseudonymousMessage(MessageId parent, Group group,
|
||||||
Author author, PrivateKey privateKey, String contentType,
|
Author author, PrivateKey privateKey, String contentType,
|
||||||
byte[] body) throws IOException, GeneralSecurityException {
|
long timestamp, byte[] body) throws IOException,
|
||||||
|
GeneralSecurityException {
|
||||||
return createMessage(parent, group, author, privateKey, contentType,
|
return createMessage(parent, group, author, privateKey, contentType,
|
||||||
body);
|
timestamp, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message createMessage(MessageId parent, Group group, Author author,
|
private Message createMessage(MessageId parent, Group group, Author author,
|
||||||
PrivateKey privateKey, String contentType, byte[] body)
|
PrivateKey privateKey, String contentType, long timestamp,
|
||||||
throws IOException, GeneralSecurityException {
|
byte[] body) throws IOException, GeneralSecurityException {
|
||||||
// Validate the arguments
|
// Validate the arguments
|
||||||
if((author == null) != (privateKey == null))
|
if((author == null) != (privateKey == null))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
@@ -102,7 +102,6 @@ class MessageFactoryImpl implements MessageFactory {
|
|||||||
if(author == null) w.writeNull();
|
if(author == null) w.writeNull();
|
||||||
else writeAuthor(w, author);
|
else writeAuthor(w, author);
|
||||||
w.writeString(contentType);
|
w.writeString(contentType);
|
||||||
long timestamp = clock.currentTimeMillis();
|
|
||||||
w.writeIntAny(timestamp);
|
w.writeIntAny(timestamp);
|
||||||
byte[] salt = new byte[MESSAGE_SALT_LENGTH];
|
byte[] salt = new byte[MESSAGE_SALT_LENGTH];
|
||||||
random.nextBytes(salt);
|
random.nextBytes(salt);
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
|||||||
private final Message message, message1;
|
private final Message message, message1;
|
||||||
private final String authorName = "Alice";
|
private final String authorName = "Alice";
|
||||||
private final String contentType = "text/plain";
|
private final String contentType = "text/plain";
|
||||||
|
private final long timestamp = System.currentTimeMillis();
|
||||||
private final String messageBody = "Hello world";
|
private final String messageBody = "Hello world";
|
||||||
private final Collection<MessageId> messageIds;
|
private final Collection<MessageId> messageIds;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
@@ -104,9 +105,9 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
|||||||
// Create two messages to the group: one anonymous, one pseudonymous
|
// Create two messages to the group: one anonymous, one pseudonymous
|
||||||
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
|
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
|
||||||
message = messageFactory.createAnonymousMessage(null, group,
|
message = messageFactory.createAnonymousMessage(null, group,
|
||||||
contentType, messageBody.getBytes("UTF-8"));
|
contentType, timestamp, messageBody.getBytes("UTF-8"));
|
||||||
message1 = messageFactory.createPseudonymousMessage(null, group,
|
message1 = messageFactory.createPseudonymousMessage(null, group,
|
||||||
author, authorKeyPair.getPrivate(), contentType,
|
author, authorKeyPair.getPrivate(), contentType, timestamp,
|
||||||
messageBody.getBytes("UTF-8"));
|
messageBody.getBytes("UTF-8"));
|
||||||
messageIds = Arrays.asList(message.getId(), message1.getId());
|
messageIds = Arrays.asList(message.getId(), message1.getId());
|
||||||
// Create some transport properties
|
// Create some transport properties
|
||||||
|
|||||||
@@ -131,9 +131,10 @@ public class ConstantsTest extends BriarTestCase {
|
|||||||
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
|
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
|
||||||
String contentType =
|
String contentType =
|
||||||
TestUtils.createRandomString(MAX_CONTENT_TYPE_LENGTH);
|
TestUtils.createRandomString(MAX_CONTENT_TYPE_LENGTH);
|
||||||
|
long timestamp = Long.MAX_VALUE;
|
||||||
byte[] body = new byte[MAX_BODY_LENGTH];
|
byte[] body = new byte[MAX_BODY_LENGTH];
|
||||||
Message message = messageFactory.createPseudonymousMessage(parent,
|
Message message = messageFactory.createPseudonymousMessage(parent,
|
||||||
group, author, privateKey, contentType, body);
|
group, author, privateKey, contentType, timestamp, body);
|
||||||
// Check the size of the serialised message
|
// Check the size of the serialised message
|
||||||
int length = message.getSerialised().length;
|
int length = message.getSerialised().length;
|
||||||
assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
|
assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
|
||||||
|
|||||||
@@ -123,10 +123,11 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
km.endpointAdded(ep, LATENCY, initialSecret.clone());
|
km.endpointAdded(ep, LATENCY, initialSecret.clone());
|
||||||
// Send Bob a message
|
// Send Bob a message
|
||||||
String contentType = "text/plain";
|
String contentType = "text/plain";
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
byte[] body = "Hi Bob!".getBytes("UTF-8");
|
byte[] body = "Hi Bob!".getBytes("UTF-8");
|
||||||
MessageFactory messageFactory = alice.getInstance(MessageFactory.class);
|
MessageFactory messageFactory = alice.getInstance(MessageFactory.class);
|
||||||
Message message = messageFactory.createPrivateMessage(null, contentType,
|
Message message = messageFactory.createPrivateMessage(null, contentType,
|
||||||
body);
|
timestamp, body);
|
||||||
db.addLocalPrivateMessage(message, contactId);
|
db.addLocalPrivateMessage(message, contactId);
|
||||||
// Create an outgoing simplex connection
|
// Create an outgoing simplex connection
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
|||||||
Reference in New Issue
Block a user