diff --git a/BUILDING.md b/BUILDING.md index 98af21e49..37bf82e94 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -27,7 +27,7 @@ install Rust tooling (read sections below) and the [dcrpcgen tool](https://githu then generate the code running the script: ``` -./scripts/generate-rpc-bindings.sh +./scripts/update-rpc-bindings.sh ``` ## Build Using Nix diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fc5b60ba..273d58d3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Leave groups and channels before deletion * Further minimize metadata in messages and while getting in contact. * Increase resilience of multi-relay usage: if on relay goes down, messages are still received in the others. +* Allow to hide a relay from contacts instead of removing, allowing smoother relay changes * Fix: keep original sent timestamp for resent messages * Fix: make clicking on broadcast member-added messages work always * Fix: remove notification when a message is deleted by sender diff --git a/jni/dc_wrapper.c b/jni/dc_wrapper.c index eccca6f8a..5b3d1f67e 100644 --- a/jni/dc_wrapper.c +++ b/jni/dc_wrapper.c @@ -946,18 +946,6 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getContactEncrInfo(JNIEnv *e } -JNIEXPORT jstring Java_com_b44t_messenger_DcContext_initiateKeyTransfer(JNIEnv *env, jobject obj) -{ - jstring setup_code = NULL; - char* temp = dc_initiate_key_transfer(get_dc_context(env, obj)); - if (temp) { - setup_code = JSTRING_NEW(temp); - dc_str_unref(temp); - } - return setup_code; -} - - JNIEXPORT void Java_com_b44t_messenger_DcContext_imex(JNIEnv *env, jobject obj, jint what, jstring dir) { CHAR_REF(dir); @@ -1631,15 +1619,6 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_hasHtml(JNIEnv *env, jobject ob } -JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getSetupCodeBegin(JNIEnv *env, jobject obj) -{ - char* temp = dc_msg_get_setupcodebegin(get_dc_msg(env, obj)); - jstring ret = JSTRING_NEW(temp); - dc_str_unref(temp); - return ret; -} - - JNIEXPORT void Java_com_b44t_messenger_DcMsg_setText(JNIEnv *env, jobject obj, jstring text) { CHAR_REF(text); diff --git a/scripts/update-rpc-bindings.sh b/scripts/update-rpc-bindings.sh index b3b16a04f..7998fb305 100755 --- a/scripts/update-rpc-bindings.sh +++ b/scripts/update-rpc-bindings.sh @@ -9,3 +9,4 @@ cd "$ROOT_DIR" # generate code dcrpcgen java --schema schema.json -o ./src/main/java/ +rm schema.json diff --git a/src/main/java/chat/delta/rpc/Rpc.java b/src/main/java/chat/delta/rpc/Rpc.java index e6444ab29..0eebb7925 100644 --- a/src/main/java/chat/delta/rpc/Rpc.java +++ b/src/main/java/chat/delta/rpc/Rpc.java @@ -289,6 +289,7 @@ public class Rpc { * from a server encoded in a QR code. * - [Self::list_transports()] to get a list of all configured transports. * - [Self::delete_transport()] to remove a transport. + * - [Self::set_transport_unpublished()] to set whether contacts see this transport. */ public void addOrUpdateTransport(Integer accountId, EnteredLoginParam param) throws RpcException { transport.call("add_or_update_transport", mapper.valueToTree(accountId), mapper.valueToTree(param)); @@ -312,11 +313,22 @@ public class Rpc { * Returns the list of all email accounts that are used as a transport in the current profile. * Use [Self::add_or_update_transport()] to add or change a transport * and [Self::delete_transport()] to delete a transport. + * Use [Self::list_transports_ex()] to additionally query + * whether the transports are marked as 'unpublished'. */ public java.util.List listTransports(Integer accountId) throws RpcException { return transport.callForResult(new TypeReference>(){}, "list_transports", mapper.valueToTree(accountId)); } + /** + * Returns the list of all email accounts that are used as a transport in the current profile. + * Use [Self::add_or_update_transport()] to add or change a transport + * and [Self::delete_transport()] to delete a transport. + */ + public java.util.List listTransportsEx(Integer accountId) throws RpcException { + return transport.callForResult(new TypeReference>(){}, "list_transports_ex", mapper.valueToTree(accountId)); + } + /** * Removes the transport with the specified email address * (i.e. [EnteredLoginParam::addr]). @@ -325,6 +337,22 @@ public class Rpc { transport.call("delete_transport", mapper.valueToTree(accountId), mapper.valueToTree(addr)); } + /** + * Change whether the transport is unpublished. + *

+ * Unpublished transports are not advertised to contacts, + * and self-sent messages are not sent there, + * so that we don't cause extra messages to the corresponding inbox, + * but can still receive messages from contacts who don't know our new transport addresses yet. + *

+ * The default is false, but when the user updates from a version that didn't have this flag, + * existing secondary transports are set to unpublished, + * so that an existing transport address doesn't suddenly get spammed with a lot of messages. + */ + public void setTransportUnpublished(Integer accountId, String addr, Boolean unpublished) throws RpcException { + transport.call("set_transport_unpublished", mapper.valueToTree(accountId), mapper.valueToTree(addr), mapper.valueToTree(unpublished)); + } + /** Signal an ongoing process to stop. */ public void stopOngoingProcess(Integer accountId) throws RpcException { transport.call("stop_ongoing_process", mapper.valueToTree(accountId)); @@ -410,14 +438,6 @@ public class Rpc { return transport.callForResult(new TypeReference(){}, "estimate_auto_deletion_count", mapper.valueToTree(accountId), mapper.valueToTree(fromServer), mapper.valueToTree(seconds)); } - public String initiateAutocryptKeyTransfer(Integer accountId) throws RpcException { - return transport.callForResult(new TypeReference(){}, "initiate_autocrypt_key_transfer", mapper.valueToTree(accountId)); - } - - public void continueAutocryptKeyTransfer(Integer accountId, Integer messageId, String setupCode) throws RpcException { - transport.call("continue_autocrypt_key_transfer", mapper.valueToTree(accountId), mapper.valueToTree(messageId), mapper.valueToTree(setupCode)); - } - public java.util.List getChatlistEntries(Integer accountId, Integer listFlags, String queryString, Integer queryContactId) throws RpcException { return transport.callForResult(new TypeReference>(){}, "get_chatlist_entries", mapper.valueToTree(accountId), mapper.valueToTree(listFlags), mapper.valueToTree(queryString), mapper.valueToTree(queryContactId)); } diff --git a/src/main/java/chat/delta/rpc/types/EventType.java b/src/main/java/chat/delta/rpc/types/EventType.java index a77bae4a6..87cb85e08 100644 --- a/src/main/java/chat/delta/rpc/types/EventType.java +++ b/src/main/java/chat/delta/rpc/types/EventType.java @@ -387,6 +387,8 @@ public abstract class EventType { public static class IncomingCallAccepted extends EventType { /** ID of the chat which the message belongs to. */ public Integer chat_id; + /** The call was accepted from this device (process). */ + public Boolean from_this_device; /** ID of the info message referring to the call. */ public Integer msg_id; } diff --git a/src/main/java/chat/delta/rpc/types/Message.java b/src/main/java/chat/delta/rpc/types/Message.java index 2087e6e0d..0d0773f6d 100644 --- a/src/main/java/chat/delta/rpc/types/Message.java +++ b/src/main/java/chat/delta/rpc/types/Message.java @@ -32,7 +32,6 @@ public class Message { public Boolean isEdited; public Boolean isForwarded; public Boolean isInfo; - public Boolean isSetupmessage; @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET) public Integer originalMsgId; @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET) @@ -47,8 +46,6 @@ public class Message { @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET) public Integer savedMessageId; public Contact sender; - @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET) - public String setupCodeBegin; /** * True if the message was correctly encrypted&signed, false otherwise. Historically, UIs showed a small padlock on the message then. *

diff --git a/src/main/java/chat/delta/rpc/types/MessageLoadResult.java b/src/main/java/chat/delta/rpc/types/MessageLoadResult.java index 80fb198b2..6c47cec7d 100644 --- a/src/main/java/chat/delta/rpc/types/MessageLoadResult.java +++ b/src/main/java/chat/delta/rpc/types/MessageLoadResult.java @@ -42,7 +42,6 @@ public abstract class MessageLoadResult { public Boolean isEdited; public Boolean isForwarded; public Boolean isInfo; - public Boolean isSetupmessage; @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET) public Integer originalMsgId; @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET) @@ -57,8 +56,6 @@ public abstract class MessageLoadResult { @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET) public Integer savedMessageId; public Contact sender; - @com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET) - public String setupCodeBegin; /** * True if the message was correctly encrypted&signed, false otherwise. Historically, UIs showed a small padlock on the message then. *

diff --git a/src/main/java/chat/delta/rpc/types/TransportListEntry.java b/src/main/java/chat/delta/rpc/types/TransportListEntry.java new file mode 100644 index 000000000..8295a6862 --- /dev/null +++ b/src/main/java/chat/delta/rpc/types/TransportListEntry.java @@ -0,0 +1,9 @@ +/* Autogenerated file, do not edit manually */ +package chat.delta.rpc.types; + +public class TransportListEntry { + /** Whether this transport is set to 'unpublished'. See `set_transport_unpublished` / `setTransportUnpublished` for details. */ + public Boolean isUnpublished; + /** The login data entered by the user. */ + public EnteredLoginParam param; +} \ No newline at end of file diff --git a/src/main/java/com/b44t/messenger/DcContext.java b/src/main/java/com/b44t/messenger/DcContext.java index 2dc7726e8..d972001b9 100644 --- a/src/main/java/com/b44t/messenger/DcContext.java +++ b/src/main/java/com/b44t/messenger/DcContext.java @@ -171,8 +171,6 @@ public class DcContext { public native String getConnectivityHtml(); - public native String initiateKeyTransfer(); - public native void imex(int what, String dir); public native String imexHasBackup(String dir); diff --git a/src/main/java/com/b44t/messenger/DcMsg.java b/src/main/java/com/b44t/messenger/DcMsg.java index a37446526..c165d9781 100644 --- a/src/main/java/com/b44t/messenger/DcMsg.java +++ b/src/main/java/com/b44t/messenger/DcMsg.java @@ -25,7 +25,6 @@ public class DcMsg { public static final int DC_INFO_GROUP_IMAGE_CHANGED = 3; public static final int DC_INFO_MEMBER_ADDED_TO_GROUP = 4; public static final int DC_INFO_MEMBER_REMOVED_FROM_GROUP = 5; - public static final int DC_INFO_AUTOCRYPT_SETUP_MESSAGE = 6; public static final int DC_INFO_SECURE_JOIN_MESSAGE = 7; public static final int DC_INFO_LOCATIONSTREAMING_ENABLED = 8; public static final int DC_INFO_LOCATION_ONLY = 9; @@ -187,8 +186,6 @@ public class DcMsg { public native boolean hasHtml(); - public native String getSetupCodeBegin(); - public native void setText(String text); public native void setFileAndDeduplicate(String file, String name, String filemime); diff --git a/src/main/java/org/thoughtcrime/securesms/relay/RelayListActivity.java b/src/main/java/org/thoughtcrime/securesms/relay/RelayListActivity.java index f8876b0ed..813be4e38 100644 --- a/src/main/java/org/thoughtcrime/securesms/relay/RelayListActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/relay/RelayListActivity.java @@ -16,7 +16,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import chat.delta.rpc.Rpc; import chat.delta.rpc.RpcException; -import chat.delta.rpc.types.EnteredLoginParam; +import chat.delta.rpc.types.TransportListEntry; import com.b44t.messenger.DcContext; import com.b44t.messenger.DcEvent; import com.google.zxing.integration.android.IntentIntegrator; @@ -53,7 +53,7 @@ public class RelayListActivity extends BaseActionBarActivity private ActivityResultLauncher qrScannerLauncher; /** Relay selected for context menu via onRelayLongClick() */ - private EnteredLoginParam contextMenuRelay = null; + private TransportListEntry contextMenuRelay = null; @Override protected void onCreate(Bundle savedInstanceState) { @@ -162,7 +162,7 @@ public class RelayListActivity extends BaseActionBarActivity String finalMainRelayAddr = mainRelayAddr; try { - List relays = rpc.listTransports(accId); + List relays = rpc.listTransportsEx(accId); Util.runOnMain(() -> adapter.setRelays(relays, finalMainRelayAddr)); } catch (RpcException e) { @@ -173,12 +173,12 @@ public class RelayListActivity extends BaseActionBarActivity } @Override - public void onRelayClick(EnteredLoginParam relay) { - if (relay.addr != null && !relay.addr.equals(adapter.getMainRelay())) { + public void onRelayClick(TransportListEntry relay) { + if (relay.param.addr != null && !relay.param.addr.equals(adapter.getMainRelay())) { Util.runOnAnyBackgroundThread( () -> { try { - rpc.setConfig(accId, DcHelper.CONFIG_CONFIGURED_ADDRESS, relay.addr); + rpc.setConfig(accId, DcHelper.CONFIG_CONFIGURED_ADDRESS, relay.param.addr); } catch (RpcException e) { Log.e(TAG, "RPC.setConfig() failed", e); } @@ -189,7 +189,7 @@ public class RelayListActivity extends BaseActionBarActivity } @Override - public void onRelayLongClick(View view, EnteredLoginParam relay) { + public void onRelayLongClick(View view, TransportListEntry relay) { contextMenuRelay = relay; registerForContextMenu(view); openContextMenu(view); @@ -201,8 +201,10 @@ public class RelayListActivity extends BaseActionBarActivity super.onCreateContextMenu(menu, v, menuInfo); getMenuInflater().inflate(R.menu.relay_item_context, menu); - boolean nonNullAddr = contextMenuRelay != null && contextMenuRelay.addr != null; - boolean isMain = nonNullAddr && contextMenuRelay.addr.equals(adapter.getMainRelay()); + boolean nonNullAddr = contextMenuRelay != null && contextMenuRelay.param.addr != null; + boolean isMain = nonNullAddr && contextMenuRelay.param.addr.equals(adapter.getMainRelay()); + + Util.redMenuItem(menu, R.id.menu_delete_relay); menu.findItem(R.id.menu_delete_relay).setVisible(!isMain); } @@ -230,28 +232,40 @@ public class RelayListActivity extends BaseActionBarActivity return super.onContextItemSelected(item); } - private void onRelayEdit(EnteredLoginParam relay) { + private void onRelayEdit(TransportListEntry relay) { Intent intent = new Intent(this, EditRelayActivity.class); - intent.putExtra(EditRelayActivity.EXTRA_ADDR, relay.addr); + intent.putExtra(EditRelayActivity.EXTRA_ADDR, relay.param.addr); startActivity(intent); } - private void onRelayDelete(EnteredLoginParam relay) { - new AlertDialog.Builder(this) - .setTitle(R.string.remove_transport) - .setMessage(getString(R.string.confirm_remove_transport, relay.addr)) - .setPositiveButton( - R.string.ok, - (dialog, which) -> { - try { - rpc.deleteTransport(accId, relay.addr); - loadRelays(); - } catch (RpcException e) { - Log.e(TAG, "RPC.deleteTransport() failed", e); - } - }) - .setNegativeButton(R.string.cancel, null) - .show(); + private void onRelayDelete(TransportListEntry relay) { + AlertDialog dialog = + new AlertDialog.Builder(this) + .setTitle(R.string.remove_transport) + .setMessage(getString(R.string.confirm_remove_or_hide_transport_x, relay.param.addr)) + .setPositiveButton( + R.string.remove_transport, + (d, which) -> { + try { + rpc.deleteTransport(accId, relay.param.addr); + loadRelays(); + } catch (RpcException e) { + Log.e(TAG, "RPC.deleteTransport() failed", e); + } + }) + .setNegativeButton(R.string.cancel, null) + .setNeutralButton( + R.string.hide_from_contacts, + (d, which) -> { + try { + rpc.setTransportUnpublished(accId, relay.param.addr, true); + loadRelays(); + } catch (RpcException e) { + Log.e(TAG, "cannot unpublish relay: ", e); + } + }) + .show(); + Util.redPositiveButton(dialog); } @Override diff --git a/src/main/java/org/thoughtcrime/securesms/relay/RelayListAdapter.java b/src/main/java/org/thoughtcrime/securesms/relay/RelayListAdapter.java index 7f7d4d0a8..72389b276 100644 --- a/src/main/java/org/thoughtcrime/securesms/relay/RelayListAdapter.java +++ b/src/main/java/org/thoughtcrime/securesms/relay/RelayListAdapter.java @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.relay; +import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -8,21 +9,21 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; -import chat.delta.rpc.types.EnteredLoginParam; +import chat.delta.rpc.types.TransportListEntry; import java.util.ArrayList; import java.util.List; import org.thoughtcrime.securesms.R; public class RelayListAdapter extends RecyclerView.Adapter { - private List relays = new ArrayList<>(); + private List relays = new ArrayList<>(); private final OnRelayClickListener listener; private String mainRelayAddr; public interface OnRelayClickListener { - void onRelayClick(EnteredLoginParam relay); + void onRelayClick(TransportListEntry relay); - void onRelayLongClick(View view, EnteredLoginParam relay); + void onRelayLongClick(View view, TransportListEntry relay); } public RelayListAdapter(OnRelayClickListener listener) { @@ -33,7 +34,7 @@ public class RelayListAdapter extends RecyclerView.Adapter relays, String mainRelayAddr) { + public void setRelays(@Nullable List relays, String mainRelayAddr) { this.relays = relays != null ? relays : new ArrayList<>(); this.mainRelayAddr = mainRelayAddr; notifyDataSetChanged(); @@ -49,8 +50,8 @@ public class RelayListAdapter extends RecyclerView.AdapterAdd Relay Remove Relay Edit Relay + Used for sending + Hide from Contacts + Hidden from contacts + + Messages are received on all relays. Add this relay? The scanned QR code does not contain a valid relay. - Remove relay \"%1$s\"?\n\nYour contacts can only reach you if you\'ve - previously contacted them via another relay.\n\nIf in doubt, remove the relay later. - + Remove relay \"%1$s\"?\n\nContacts who only know this relay may not reach + you until you message them again.\n\nIf in doubt, hide it rather than removing it. Certificate Checks Please enter a valid email address Please enter a valid server / IP address