mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
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:
parent
6a9f791f0d
commit
5ee375c74d
2 changed files with 163 additions and 92 deletions
|
@ -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 user’s 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();
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue