Store encrypted messages on disk when receiving them

- Acknowledge to the server only after the message is stored.
- Delete the message when decrypting was successful
This commit is contained in:
AsamK 2016-08-20 16:01:31 +02:00
parent 6a9f791f0d
commit 5ee375c74d
2 changed files with 163 additions and 92 deletions

View file

@ -768,7 +768,7 @@ public class Main {
} }
@Override @Override
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content) { public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
SignalServiceAddress source = envelope.getSourceAddress(); SignalServiceAddress source = envelope.getSourceAddress();
ContactInfo sourceContact = m.getContact(source.getNumber()); ContactInfo sourceContact = m.getContact(source.getNumber());
System.out.println(String.format("Envelope from: %s (device: %d)", (sourceContact == null ? "" : "" + sourceContact.name + "") + source.getNumber(), envelope.getSourceDevice())); System.out.println(String.format("Envelope from: %s (device: %d)", (sourceContact == null ? "" : "" + sourceContact.name + "") + source.getNumber(), envelope.getSourceDevice()));
@ -780,6 +780,16 @@ public class Main {
if (envelope.isReceipt()) { if (envelope.isReceipt()) {
System.out.println("Got receipt."); System.out.println("Got receipt.");
} else if (envelope.isSignalMessage() | envelope.isPreKeySignalMessage()) { } else if (envelope.isSignalMessage() | envelope.isPreKeySignalMessage()) {
if (exception != null) {
if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
org.whispersystems.libsignal.UntrustedIdentityException e = (org.whispersystems.libsignal.UntrustedIdentityException) exception;
System.out.println("The users key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
System.out.println("Use 'signal-cli -u " + m.getUsername() + " listIdentities -n " + e.getName() + "', verify the key and run 'signal-cli -u " + m.getUsername() + " trust -v \"FINGER_PRINT\" " + e.getName() + "' to mark it as trusted");
System.out.println("If you don't care about security, use 'signal-cli -u " + m.getUsername() + " trust -a " + e.getName() + "' to trust it without verification");
} else {
System.out.println("Exception: " + exception.getMessage() + " (" + exception.getClass().getSimpleName() + ")");
}
}
if (content == null) { if (content == null) {
System.out.println("Failed to decrypt message."); System.out.println("Failed to decrypt message.");
} else { } else {
@ -904,8 +914,8 @@ public class Main {
} }
@Override @Override
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content) { public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
super.handleMessage(envelope, content); super.handleMessage(envelope, content, exception);
if (!envelope.isReceipt() && content != null && content.getDataMessage().isPresent()) { if (!envelope.isReceipt() && content != null && content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get(); SignalServiceDataMessage message = content.getDataMessage().get();

View file

@ -133,6 +133,20 @@ class Manager implements Signal {
return dataPath + "/" + username; return dataPath + "/" + username;
} }
private String getMessageCachePath() {
return this.dataPath + "/" + username + ".d/msg-cache";
}
private String getMessageCachePath(String sender) {
return getMessageCachePath() + "/" + sender.replace("/", "_");
}
private File getMessageCacheFile(String sender, long now, long timestamp) throws IOException {
String cachePath = getMessageCachePath(sender);
createPrivateDirectories(cachePath);
return new File(cachePath + "/" + now + "_" + timestamp);
}
private static void createPrivateDirectories(String path) throws IOException { private static void createPrivateDirectories(String path) throws IOException {
final Path file = new File(path).toPath(); final Path file = new File(path).toPath();
try { try {
@ -778,11 +792,8 @@ class Manager implements Signal {
try { try {
return cipher.decrypt(envelope); return cipher.decrypt(envelope);
} catch (org.whispersystems.libsignal.UntrustedIdentityException e) { } catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
// TODO temporarily store message, until user has accepted the key
signalProtocolStore.saveIdentity(e.getName(), e.getUntrustedIdentity(), TrustLevel.UNTRUSTED); signalProtocolStore.saveIdentity(e.getName(), e.getUntrustedIdentity(), TrustLevel.UNTRUSTED);
throw e; throw e;
} catch (Exception e) {
throw e;
} }
} }
@ -791,7 +802,7 @@ class Manager implements Signal {
} }
public interface ReceiveMessageHandler { public interface ReceiveMessageHandler {
void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent decryptedContent); void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent decryptedContent, Throwable e);
} }
private void handleSignalServiceDataMessage(SignalServiceDataMessage message, boolean isSync, String source, String destination) { private void handleSignalServiceDataMessage(SignalServiceDataMessage message, boolean isSync, String source, String destination) {
@ -863,17 +874,56 @@ class Manager implements Signal {
while (true) { while (true) {
SignalServiceEnvelope envelope; SignalServiceEnvelope envelope;
SignalServiceContent content = null; SignalServiceContent content = null;
Exception exception = null;
final long now = new Date().getTime();
try { try {
envelope = messagePipe.read(timeoutSeconds, TimeUnit.SECONDS); envelope = messagePipe.read(timeoutSeconds, TimeUnit.SECONDS, new SignalServiceMessagePipe.MessagePipeCallback() {
@Override
public void onMessage(SignalServiceEnvelope envelope) {
// store message on disk, before acknowledging receipt to the server
try {
File cacheFile = getMessageCacheFile(envelope.getSource(), now, envelope.getTimestamp());
storeEnvelope(envelope, cacheFile);
} catch (IOException e) {
System.err.println("Failed to store encrypted message in disk cache, ignoring: " + e.getMessage());
}
}
});
} catch (TimeoutException e) {
if (returnOnTimeout)
return;
continue;
} catch (InvalidVersionException e) {
System.err.println("Ignoring error: " + e.getMessage());
continue;
}
if (!envelope.isReceipt()) { if (!envelope.isReceipt()) {
Exception exception;
try { try {
content = decryptMessage(envelope); content = decryptMessage(envelope);
} catch (Exception e) { } catch (Exception e) {
exception = e; exception = e;
// TODO pass exception to handler instead
e.printStackTrace();
} }
handleMessage(envelope, content);
}
save();
handler.handleMessage(envelope, content, exception);
if (exception == null || !(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) {
try {
File cacheFile = getMessageCacheFile(envelope.getSource(), now, envelope.getTimestamp());
cacheFile.delete();
} catch (IOException e) {
// Ignoring
return;
}
}
}
} finally {
if (messagePipe != null)
messagePipe.shutdown();
}
}
private void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content) {
if (content != null) { if (content != null) {
if (content.getDataMessage().isPresent()) { if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get(); SignalServiceDataMessage message = content.getDataMessage().get();
@ -890,14 +940,14 @@ class Manager implements Signal {
if (rm.isContactsRequest()) { if (rm.isContactsRequest()) {
try { try {
sendContacts(); sendContacts();
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException | IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
if (rm.isGroupsRequest()) { if (rm.isGroupsRequest()) {
try { try {
sendGroups(); sendGroups();
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException | IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@ -949,18 +999,29 @@ class Manager implements Signal {
} }
} }
} }
save();
handler.handleMessage(envelope, content); private void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException {
} catch (TimeoutException e) { try (FileOutputStream f = new FileOutputStream(file)) {
if (returnOnTimeout) DataOutputStream out = new DataOutputStream(f);
return; out.writeInt(1); // version
} catch (InvalidVersionException e) { out.writeInt(envelope.getType());
System.err.println("Ignoring error: " + e.getMessage()); out.writeUTF(envelope.getSource());
out.writeInt(envelope.getSourceDevice());
out.writeUTF(envelope.getRelay());
out.writeLong(envelope.getTimestamp());
if (envelope.hasContent()) {
out.writeInt(envelope.getContent().length);
out.write(envelope.getContent());
} else {
out.writeInt(0);
} }
if (envelope.hasLegacyMessage()) {
out.writeInt(envelope.getLegacyMessage().length);
out.write(envelope.getLegacyMessage());
} else {
out.writeInt(0);
} }
} finally { out.close();
if (messagePipe != null)
messagePipe.shutdown();
} }
} }