From 973a48ef626c884b015487d8919ab8cb043f8e4a Mon Sep 17 00:00:00 2001 From: kosyak Date: Mon, 30 Sep 2024 05:47:13 +0200 Subject: [PATCH] otr conversations implementation --- .../conversations/entities/Conversation.java | 31 +++++++++++----- .../conversations/parser/MessageParser.java | 8 +++-- .../persistance/DatabaseBackend.java | 35 ++++++++++++------- .../services/XmppConnectionService.java | 10 +++++- .../ui/ConversationFragment.java | 29 +++++++++++++-- .../ui/ConversationsActivity.java | 12 +++++-- .../ui/adapter/ConversationAdapter.java | 8 +++-- src/main/res/menu/fragment_conversation.xml | 5 +++ src/main/res/values/strings.xml | 2 ++ 9 files changed, 109 insertions(+), 31 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index da60498d3..29f9109b4 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -188,6 +188,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl private int mode; private JSONObject attributes; private Jid nextCounterpart; + private boolean hasPermanentCounterpart; private transient SessionImpl otrSession; private transient String otrFingerprint = null; private Smp mSmp = new Smp(); @@ -229,6 +230,9 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl this.attributes = new JSONObject(); } this.nextCounterpart = nextCounterpart; + if (nextCounterpart != null) { + hasPermanentCounterpart = true; + } } public String getContactUuid() { @@ -937,7 +941,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl values.put(STATUS, status); values.put(MODE, mode); - if (nextCounterpart != null) { + if (nextCounterpart != null && hasPermanentCounterpart) { values.put(NEXT_COUNTERPART, nextCounterpart.toString()); } @@ -963,6 +967,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl presence, "xmpp"); this.otrSession = new SessionImpl(sessionId, getAccount().getOtrService()); + android.util.Log.e("41fd", "new session " + System.identityHashCode(this), new RuntimeException()); try { if (sendStart) { this.otrSession.startSession(); @@ -981,6 +986,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } public void resetOtrSession() { + android.util.Log.e("41fd", "reset " + System.identityHashCode(this), new RuntimeException()); this.otrFingerprint = null; this.otrSession = null; this.mSmp.hint = null; @@ -1109,6 +1115,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return this.nextCounterpart; } + public boolean hasPermanentCounterpart() { + return hasPermanentCounterpart; + } + public void setNextCounterpart(Jid jid) { this.nextCounterpart = jid; } @@ -1117,6 +1127,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl if (!Config.supportOmemo() && !Config.supportOpenPgp() && !Config.supportOtr()) { return Message.ENCRYPTION_NONE; } + + if (Config.supportOtr() && nextCounterpart != null && getMode() == MODE_SINGLE && hasPermanentCounterpart) { + return Message.ENCRYPTION_OTR; + } + if (OmemoSetting.isAlways()) { return suitableForOmemoByDefault(this) ? Message.ENCRYPTION_AXOLOTL : Message.ENCRYPTION_NONE; } @@ -1127,7 +1142,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl defaultEncryption = Message.ENCRYPTION_NONE; } int encryption = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, defaultEncryption); - if (encryption == Message.ENCRYPTION_OTR || encryption < 0) { + if (encryption < 0) { return defaultEncryption; } else { return encryption; @@ -1393,14 +1408,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl String res2 = nextCounterpart == null ? null : nextCounterpart.getResource(); if (nextCounterpart == null) { - if (!message.isPrivateMessage()) { + if (!message.isPrivateMessage() && message.encryption != Message.ENCRYPTION_OTR) { synchronized (this.messages) { this.messages.add(message); actualizeReplyMessages(this.messages, List.of(message)); } } } else { - if (message.isPrivateMessage() && Objects.equals(res1, res2)) { + if ((message.isPrivateMessage() || message.encryption == Message.ENCRYPTION_OTR) && Objects.equals(res1, res2)) { synchronized (this.messages) { this.messages.add(message); actualizeReplyMessages(this.messages, List.of(message)); @@ -1422,14 +1437,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } if (nextCounterpart == null) { - if (!message.isPrivateMessage()) { + if (!message.isPrivateMessage() && message.encryption != Message.ENCRYPTION_OTR) { synchronized (this.messages) { properListToAdd.add(Math.min(offset, properListToAdd.size()), message); actualizeReplyMessages(properListToAdd, List.of(message)); } } } else { - if (message.isPrivateMessage() && Objects.equals(res1, res2)) { + if ((message.isPrivateMessage() || message.encryption == Message.ENCRYPTION_OTR) && Objects.equals(res1, res2)) { synchronized (this.messages) { properListToAdd.add(Math.min(offset, properListToAdd.size()), message); actualizeReplyMessages(properListToAdd, List.of(message)); @@ -1453,7 +1468,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl if (nextCounterpart == null) { for(Message m : messages) { - if (!m.isPrivateMessage()) { + if (!m.isPrivateMessage() && m.encryption != Message.ENCRYPTION_OTR) { newM.add(m); } } @@ -1464,7 +1479,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl String res2 = nextCounterpart == null ? null : nextCounterpart.getResource(); - if (m.isPrivateMessage() && Objects.equals(res1, res2)) { + if ((m.isPrivateMessage() || m.encryption == Message.ENCRYPTION_OTR) && Objects.equals(res1, res2)) { newM.add(m); } } diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 0c9dafcba..76593e291 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -5,6 +5,8 @@ import android.text.Html; import android.util.Log; import android.util.Pair; +import com.google.common.base.Strings; + import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; @@ -594,7 +596,9 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece final boolean conversationIsProbablyMuc = isTypeGroupChat || mucUserElement != null || account.getXmppConnection().getMucServersWithholdAccount().contains(counterpart.getDomain().toEscapedString()); - if (conversationIsProbablyMuc && !isTypeGroupChat) { + final boolean isOTR = body != null && body.content.startsWith("?OTR") && Config.supportOtr(); + + if ((conversationIsProbablyMuc && !isTypeGroupChat) || (!Strings.isNullOrEmpty(counterpart.getResource()) && isOTR)) { nextCounterpart = counterpart; } @@ -658,7 +662,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } } final Message message; - if (body != null && body.content.startsWith("?OTR") && Config.supportOtr()) { + if (isOTR) { if (!isForwarded && !isTypeGroupChat && isProperlyAddressed && !conversationMultiMode) { message = parseOtrChat(body.content, from, remoteMsgId, conversation); if (message == null) { diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index 41ab36dff..3c06561e9 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -50,6 +50,7 @@ import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.PresenceTemplate; import eu.siacs.conversations.entities.Roster; @@ -887,30 +888,40 @@ public class DatabaseBackend extends SQLiteOpenHelper { String comparsionOperation = isForward ? ">?" : " { - message.setCounterpart(conversation.getNextCounterpart()); - xmppService.sendMessage(message); - messageSent(); + Conversation c = activity.xmppConnectionService.findOrCreateConversation(conversation.getAccount(), conversation.getJid(), null, false, false, false, conversation.getNextCounterpart()); + conversation.setNextCounterpart(null); + if (c != conversation) { + activity.switchToConversation(c); + } }); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java index e98fb8f4e..a999c3bf6 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java @@ -757,8 +757,12 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio if (mainFragment instanceof ConversationFragment) { final Conversation conversation = ((ConversationFragment) mainFragment).getConversation(); if (conversation != null) { - if (conversation.getNextCounterpart() != null) { - actionBar.setTitle(getString(R.string.muc_private_conversation_title, conversation.getNextCounterpart().getResource(), conversation.getName())); + if (conversation.getNextCounterpart() != null && conversation.hasPermanentCounterpart()) { + if (conversation.getMode() == Conversational.MODE_MULTI) { + actionBar.setTitle(getString(R.string.muc_private_conversation_title, conversation.getNextCounterpart().getResource(), conversation.getName())); + } else { + actionBar.setTitle(getString(R.string.secret_chat_title, conversation.getName(), "")); + } } else { actionBar.setTitle(conversation.getName()); } @@ -780,7 +784,9 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio } else if (conversation.getMode() == Conversation.MODE_SINGLE) { Contact contact = conversation.getContact(); List statuses = contact.getPresences().getStatusMessages(); - if (!statuses.isEmpty() && !statuses.get(0).isBlank()) { + if (conversation.getNextCounterpart() != null && conversation.hasPermanentCounterpart()) { + actionBar.setSubtitle(conversation.getNextCounterpart().getResource()); + } else if (!statuses.isEmpty() && !statuses.get(0).isBlank()) { actionBar.setSubtitle(statuses.get(0)); handler.postDelayed(refreshTitleRunnable, 5000L); } else { diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java index bac12514b..d1a9a3f1f 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java @@ -338,8 +338,12 @@ public class ConversationAdapter } CharSequence name = conversation.getName(); - if (conversation.getNextCounterpart() != null) { - name = viewHolder.binding.getRoot().getResources().getString(R.string.muc_private_conversation_title, conversation.getNextCounterpart().getResource(), conversation.getName()); + if (conversation.getNextCounterpart() != null && conversation.hasPermanentCounterpart()) { + if (conversation.getMode() == Conversational.MODE_MULTI) { + name = viewHolder.binding.getRoot().getResources().getString(R.string.muc_private_conversation_title, conversation.getNextCounterpart().getResource(), conversation.getName()); + } else { + name = viewHolder.binding.getRoot().getResources().getString(R.string.secret_chat_title, conversation.getName(), conversation.getNextCounterpart().getResource()); + } } if (conversation.withSelf()) { diff --git a/src/main/res/menu/fragment_conversation.xml b/src/main/res/menu/fragment_conversation.xml index 86ea78d67..ef3f8e792 100644 --- a/src/main/res/menu/fragment_conversation.xml +++ b/src/main/res/menu/fragment_conversation.xml @@ -121,6 +121,11 @@ android:orderInCategory="60" android:title="@string/action_end_conversation" app:showAsAction="never" /> + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 8cd41d4bc..dd56a19d9 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ Manage accounts Manage account Close conversation + Start secret chat Contact details Group chat details Channel details @@ -1089,6 +1090,7 @@ resize filter could_not_create_file + %1$s (Secret Chat / %2$s) %1$s (%2$s) Note to self (%1$s) Private conversation with: