diff --git a/conversations.doap b/conversations.doap index d99e60656547ea0584b80f941ad218581c752a04..4d492f9b3faad10115bd84261a67a0e759511997 100644 --- a/conversations.doap +++ b/conversations.doap @@ -375,7 +375,7 @@ complete - 0.2 + 0.3.1 diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index 501c6d0c7d0df42da6a5f73d0b3c5a12a6e12354..c9fa7f6a6cf515638e344cbaef78da6073ebcaf9 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -344,12 +344,18 @@ public class IqGenerator extends AbstractGenerator { return iq; } - public IqPacket generateSetBlockRequest(final Jid jid, boolean reportSpam) { + public IqPacket generateSetBlockRequest(final Jid jid, final boolean reportSpam, final String serverMsgId) { final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); final Element block = iq.addChild("block", Namespace.BLOCKING); final Element item = block.addChild("item").setAttribute("jid", jid); if (reportSpam) { - item.addChild("report", "urn:xmpp:reporting:0").addChild("spam"); + final Element report = item.addChild("report", Namespace.REPORTING); + report.setAttribute("reason", Namespace.REPORTING_REASON_SPAM); + if (serverMsgId != null) { + final Element stanzaId = report.addChild("stanza-id", Namespace.STANZA_IDS); + stanzaId.setAttribute("by", jid); + stanzaId.setAttribute("id", serverMsgId); + } } Log.d(Config.LOGTAG, iq.toString()); return iq; diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 77a257403761afab5186e54ec5d3f43309b54482..d2f26af792f934508b65fdada33f2345fbb8bdf0 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -4756,10 +4756,10 @@ public class XmppConnectionService extends Service { mDatabaseWriterExecutor.execute(runnable); } - public boolean sendBlockRequest(final Blockable blockable, boolean reportSpam) { + public boolean sendBlockRequest(final Blockable blockable, final boolean reportSpam, final String serverMsgId) { if (blockable != null && blockable.getBlockedJid() != null) { final Jid jid = blockable.getBlockedJid(); - this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), (a, response) -> { + this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam, serverMsgId), (a, response) -> { if (response.getType() == IqPacket.TYPE.RESULT) { a.getBlocklist().add(jid); updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED); diff --git a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java index 6f4d77c51ffc00da35861dd727525da7f72b76cf..986aeb5639c2751873aaa820441da766905fbafe 100644 --- a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java +++ b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java @@ -14,13 +14,27 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.ui.util.JidDialog; public final class BlockContactDialog { + public static void show(final XmppActivity xmppActivity, final Blockable blockable) { + show(xmppActivity, blockable, null); + } + public static void show(final XmppActivity xmppActivity, final Blockable blockable, final String serverMsgId) { final AlertDialog.Builder builder = new AlertDialog.Builder(xmppActivity); final boolean isBlocked = blockable.isBlocked(); builder.setNegativeButton(R.string.cancel, null); DialogBlockContactBinding binding = DataBindingUtil.inflate(xmppActivity.getLayoutInflater(), R.layout.dialog_block_contact, null, false); final boolean reporting = blockable.getAccount().getXmppConnection().getFeatures().spamReporting(); - binding.reportSpam.setVisibility(!isBlocked && reporting ? View.VISIBLE : View.GONE); + if (reporting && !isBlocked) { + binding.reportSpam.setVisibility(View.VISIBLE); + if (serverMsgId != null) { + binding.reportSpam.setChecked(true); + binding.reportSpam.setEnabled(false); + } else { + binding.reportSpam.setEnabled(true); + } + } else { + binding.reportSpam.setVisibility(View.GONE); + } builder.setView(binding.getRoot()); final String value; @@ -34,8 +48,18 @@ public final class BlockContactDialog { value =blockable.getJid().getDomain().toEscapedString(); res = isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text; } else { - int resBlockAction = blockable instanceof Conversation && ((Conversation) blockable).isWithStranger() ? R.string.block_stranger : R.string.action_block_contact; - builder.setTitle(isBlocked ? R.string.action_unblock_contact : resBlockAction); + if (isBlocked) { + builder.setTitle(R.string.action_unblock_contact); + } else if (serverMsgId != null) { + builder.setTitle(R.string.report_spam_and_block); + } else { + final int resBlockAction = + blockable instanceof Conversation + && ((Conversation) blockable).isWithStranger() + ? R.string.block_stranger + : R.string.action_block_contact; + builder.setTitle(resBlockAction); + } value = blockable.getJid().asBareJid().toEscapedString(); res = isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text; } @@ -45,7 +69,7 @@ public final class BlockContactDialog { xmppActivity.xmppConnectionService.sendUnblockRequest(blockable); } else { boolean toastShown = false; - if (xmppActivity.xmppConnectionService.sendBlockRequest(blockable, binding.reportSpam.isChecked())) { + if (xmppActivity.xmppConnectionService.sendBlockRequest(blockable, binding.reportSpam.isChecked(), serverMsgId)) { Toast.makeText(xmppActivity, R.string.corresponding_conversations_closed, Toast.LENGTH_SHORT).show(); toastShown = true; } diff --git a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java index 55cf9ba0360cad1c315fb3adb7ee30556ca7622a..17a9853b231b0d534c7709d829b03246dc04245e 100644 --- a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java @@ -87,7 +87,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> { Blockable blockable = new RawBlockable(account, contactJid); - if (xmppConnectionService.sendBlockRequest(blockable, false)) { + if (xmppConnectionService.sendBlockRequest(blockable, false, null)) { Toast.makeText(BlocklistActivity.this, R.string.corresponding_conversations_closed, Toast.LENGTH_SHORT).show(); } return true; diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 2ff7d9c6f10d2ecab88e03aef6827a53827baf20..d19bc4a55cba0e01326013afb0ba68b9ada633a4 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1308,6 +1308,7 @@ public class ConversationFragment extends XmppFragment || t instanceof HttpDownloadConnection); activity.getMenuInflater().inflate(R.menu.message_context, menu); menu.setHeaderTitle(R.string.message_options); + final MenuItem reportAndBlock = menu.findItem(R.id.action_report_and_block); MenuItem openWith = menu.findItem(R.id.open_with); MenuItem copyMessage = menu.findItem(R.id.copy_message); MenuItem copyLink = menu.findItem(R.id.copy_link); @@ -1326,6 +1327,17 @@ public class ConversationFragment extends XmppFragment m.getStatus() == Message.STATUS_SEND_FAILED && m.getErrorMessage() != null && !Message.ERROR_MESSAGE_CANCELLED.equals(m.getErrorMessage()); + final Conversational conversational = m.getConversation(); + if (m.getStatus() == Message.STATUS_RECEIVED && conversational instanceof Conversation c) { + final XmppConnection connection = c.getAccount().getXmppConnection(); + if (c.isWithStranger() + && m.getServerMsgId() != null + && !c.isBlocked() + && connection != null + && connection.getFeatures().spamReporting()) { + reportAndBlock.setVisible(true); + } + } if (!m.isFileOrImage() && !encrypted && !m.isGeoUri() @@ -1449,6 +1461,9 @@ public class ConversationFragment extends XmppFragment case R.id.open_with: openWith(selectedMessage); return true; + case R.id.action_report_and_block: + reportMessage(selectedMessage); + return true; default: return super.onContextItemSelected(item); } @@ -2114,6 +2129,10 @@ public class ConversationFragment extends XmppFragment } } + private void reportMessage(final Message message) { + BlockContactDialog.show(activity, conversation.getContact(), message.getServerMsgId()); + } + private void showErrorMessage(final Message message) { AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); builder.setTitle(R.string.error_message); diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index c6730fe6c0f2262aae965581d952329911b0678c..667fa6457f4e238e1d1b02e44b33e80ae1e10869 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -67,4 +67,6 @@ public final class Namespace { public static final String OMEMO_DTLS_SRTP_VERIFICATION = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification"; public static final String JINGLE_TRANSPORT_ICE_OPTION = "http://gultsch.de/xmpp/drafts/jingle/transports/ice-udp/option"; public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push"; + public static final String REPORTING = "urn:xmpp:reporting:1"; + public static final String REPORTING_REASON_SPAM = "urn:xmpp:reporting:spam"; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 7de0de272833b259a9bd9d5b25aaf0948489ee16..4151b0187db4175c5c7965d54427e3911a79fb95 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -2689,7 +2689,7 @@ public class XmppConnection implements Runnable { } public boolean spamReporting() { - return hasDiscoFeature(account.getDomain(), "urn:xmpp:reporting:reason:spam:0"); + return hasDiscoFeature(account.getDomain(), Namespace.REPORTING); } public boolean flexibleOfflineMessageRetrieval() { diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml index f32505203e92f85efd1c705434f4454d0483955d..cb6b3124483895d81a14118fa7c1da5066758ced 100644 --- a/src/main/res/menu/message_context.xml +++ b/src/main/res/menu/message_context.xml @@ -1,6 +1,11 @@ + + Log in Your contact uses unverified devices. Scan their 2D barcode to perform verification and impede active MITM attacks. You are using unverified devices. Scan the 2D barcode on your other devices to perform verification and impede active MITM attacks. + Report spam + Report spam and block spammer