diff options
7 files changed, 137 insertions, 65 deletions
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index 6a12f407..96860a16 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.xml.Element; @@ -12,6 +13,7 @@ import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import android.annotation.SuppressLint; +import android.util.Log; @SuppressLint("DefaultLocale") public class MucOptions { @@ -51,8 +53,6 @@ public class MucOptions { } } - ; - public enum Role { MODERATOR("moderator", R.string.moderator), VISITOR("visitor", R.string.visitor), @@ -86,6 +86,7 @@ public class MucOptions { public static final int KICKED_FROM_ROOM = 9; + public static final String STATUS_CODE_ROOM_CONFIG_CHANGED = "104"; public static final String STATUS_CODE_SELF_PRESENCE = "110"; public static final String STATUS_CODE_BANNED = "301"; public static final String STATUS_CODE_CHANGED_NICK = "303"; @@ -107,8 +108,8 @@ public class MucOptions { } public class User { - private Role role; - private Affiliation affiliation; + private Role role = Role.NONE; + private Affiliation affiliation = Affiliation.NONE; private String name; private Jid jid; private long pgpKeyId = 0; @@ -190,6 +191,7 @@ public class MucOptions { private Account account; private List<User> users = new CopyOnWriteArrayList<>(); + private List<String> features = new ArrayList<>(); private Conversation conversation; private boolean isOnline = false; private int error = ERROR_UNKNOWN; @@ -205,6 +207,23 @@ public class MucOptions { this.conversation = conversation; } + public void updateFeatures(ArrayList<String> features) { + this.features.clear(); + this.features.addAll(features); + } + + public boolean hasFeature(String feature) { + return this.features.contains(feature); + } + + public boolean canInvite() { + return !membersOnly() || self.getAffiliation().ranks(Affiliation.ADMIN); + } + + public boolean membersOnly() { + return hasFeature("muc_membersonly"); + } + public void deleteUser(String name) { for (int i = 0; i < users.size(); ++i) { if (users.get(i).getName().equals(name)) { @@ -225,6 +244,7 @@ public class MucOptions { } public void processPacket(PresencePacket packet, PgpEngine pgp) { + Log.d(Config.LOGTAG, packet.toString()); final Jid from = packet.getFrom(); if (!from.isBareJid()) { final String name = from.getResourcepart(); @@ -320,7 +340,7 @@ public class MucOptions { } private List<String> getStatusCodes(Element x) { - List<String> codes = new ArrayList<String>(); + List<String> codes = new ArrayList<>(); if (x != null) { for (Element child : x.getChildren()) { if (child.getName().equals("status")) { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 65a8f9e6..bb9de061 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -1,15 +1,13 @@ package eu.siacs.conversations.parser; -import android.util.Log; - import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; -import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; @@ -142,14 +140,25 @@ public class MessageParser extends AbstractParser implements Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, from.toBareJid(), true); if (packet.hasChild("subject")) { - conversation.getMucOptions().setSubject( - packet.findChild("subject").getContent()); + conversation.getMucOptions().setSubject(packet.findChild("subject").getContent()); mXmppConnectionService.updateConversationUi(); return null; } - if (from.isBareJid()) { + + final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user"); + if (from.isBareJid() && (x == null || !x.hasChild("status"))) { return null; + } else if (from.isBareJid() && x.hasChild("status")) { + for(Element child : x.getChildren()) { + if (child.getName().equals("status")) { + String code = child.getAttribute("code"); + if (code.contains(MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED)) { + mXmppConnectionService.fetchConferenceConfiguration(conversation); + } + } + } } + if (from.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { if (mXmppConnectionService.markMessage(conversation, packet.getId(), Message.STATUS_SEND)) { @@ -348,6 +357,17 @@ public class MessageParser extends AbstractParser implements private void parseNonMessage(Element packet, Account account) { final Jid from = packet.getAttributeAsJid("from"); + Element invite = extractInvite(packet); + if (invite != null) { + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, from, true); + if (!conversation.getMucOptions().online()) { + Element password = invite.findChild("password"); + conversation.getMucOptions().setPassword(password == null ? null : password.getContent()); + mXmppConnectionService.databaseBackend.updateConversation(conversation); + mXmppConnectionService.joinMuc(conversation); + mXmppConnectionService.updateConversationUi(); + } + } if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) { Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event"); @@ -374,42 +394,18 @@ public class MessageParser extends AbstractParser implements updateLastseen(packet, account, false); mXmppConnectionService.markMessage(account, from.toBareJid(), id, Message.STATUS_SEND_RECEIVED); - } else if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) { - Element x = packet.findChild("x", - "http://jabber.org/protocol/muc#user"); - if (x.hasChild("invite")) { - Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, - packet.getAttributeAsJid("from"), true); - if (!conversation.getMucOptions().online()) { - if (x.hasChild("password")) { - Element password = x.findChild("password"); - conversation.getMucOptions().setPassword( - password.getContent()); - mXmppConnectionService.databaseBackend - .updateConversation(conversation); - } - mXmppConnectionService.joinMuc(conversation); - mXmppConnectionService.updateConversationUi(); - } - } - } else if (packet.hasChild("x", "jabber:x:conference")) { - Element x = packet.findChild("x", "jabber:x:conference"); - Jid jid = x.getAttributeAsJid("jid"); - String password = x.getAttribute("password"); - if (jid != null) { - Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, jid, true); - if (!conversation.getMucOptions().online()) { - if (password != null) { - conversation.getMucOptions().setPassword(password); - mXmppConnectionService.databaseBackend - .updateConversation(conversation); - } - mXmppConnectionService.joinMuc(conversation); - mXmppConnectionService.updateConversationUi(); - } - } + } + } + + private Element extractInvite(Element message) { + Element x = message.findChild("x","http://jabber.org/protocol/muc#user"); + if (x == null) { + x = message.findChild("x","jabber:x:conference"); + } + if (x != null && x.hasChild("invite")) { + return x; + } else { + return null; } } @@ -493,7 +489,7 @@ public class MessageParser extends AbstractParser implements if (message != null) { message.markUnread(); } - } else if (packet.hasChild("body")) { + } else if (packet.hasChild("body") && extractInvite(packet) == null) { message = this.parseChat(packet, account); if (message != null) { message.markUnread(); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 4ab2d42b..5c16ef3f 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1316,6 +1316,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa packet.addChild("x", "jabber:x:signed").setContent(sig); } sendPresencePacket(account, packet); + fetchConferenceConfiguration(conversation); if (!joinJid.equals(conversation.getJid())) { conversation.setContactJid(joinJid); databaseBackend.updateConversation(conversation); @@ -1475,6 +1476,29 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + public void fetchConferenceConfiguration(final Conversation conversation) { + IqPacket request = new IqPacket(IqPacket.TYPE.GET); + request.setTo(conversation.getJid().toBareJid()); + request.query("http://jabber.org/protocol/disco#info"); + sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() != IqPacket.TYPE.ERROR) { + ArrayList<String> features = new ArrayList<String>(); + for (Element child : packet.query().getChildren()) { + if (child != null && child.getName().equals("feature")) { + String var = child.getAttribute("var"); + if (var != null) { + features.add(var); + } + } + } + conversation.getMucOptions().updateFeatures(features); + } + } + }); + } + public void pushConferenceConfiguration(final Conversation conversation,final Bundle options, final OnConferenceOptionsPushed callback) { IqPacket request = new IqPacket(IqPacket.TYPE.GET); request.setTo(conversation.getJid().toBareJid()); @@ -1520,14 +1544,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa final Jid jid = user.toBareJid(); IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString()); Log.d(Config.LOGTAG,request.toString()); - sendIqPacket(conference.getAccount(),request,new OnIqPacketReceived() { + sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - Log.d(Config.LOGTAG,packet.toString()); + Log.d(Config.LOGTAG, packet.toString()); if (packet.getType() == IqPacket.TYPE.RESULT) { callback.onAffiliationChangedSuccessful(jid); } else { - callback.onAffiliationChangeFailed(jid,R.string.could_not_change_affiliation); + callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation); } } }); diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index 41190ef2..5272c922 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -1,8 +1,10 @@ package eu.siacs.conversations.ui; import android.annotation.TargetApi; +import android.app.AlertDialog; import android.app.PendingIntent; import android.content.Context; +import android.content.DialogInterface; import android.content.IntentSender.SendIntentException; import android.graphics.Bitmap; import android.os.Build; @@ -285,13 +287,31 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this); return true; case R.id.remove_from_room: - xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.OUTCAST,this); + removeFromRoom(mSelectedUser); return true; default: return super.onContextItemSelected(item); } } + private void removeFromRoom(final User user) { + if (mConversation.getMucOptions().membersOnly()) { + xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.NONE,this); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.ban_user_from_conference); + builder.setMessage(getString(R.string.removing_from_public_conference,user.getName())); + builder.setNegativeButton(R.string.cancel,null); + builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this); + } + }); + builder.create().show(); + } + } + protected void startConversation(User user) { if (user.getJid() != null) { Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getJid().toBareJid(),false); @@ -397,6 +417,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers ImageView iv = (ImageView) view.findViewById(R.id.contact_photo); iv.setImageBitmap(bm); membersView.addView(view); + if (mConversation.getMucOptions().canInvite()) { + mInviteButton.setVisibility(View.VISIBLE); + } else { + mInviteButton.setVisibility(View.GONE); + } } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 795dabf2..ba5b3e59 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -9,7 +9,6 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.IntentSender.SendIntentException; -import android.media.MediaActionSound; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -83,11 +82,6 @@ public class ConversationActivity extends XmppActivity private boolean mActivityPaused = false; - - public List<Conversation> getConversationList() { - return this.conversationList; - } - public Conversation getSelectedConversation() { return this.mSelectedConversation; } @@ -284,8 +278,7 @@ public class ConversationActivity extends XmppActivity final MenuItem menuBlock = menu.findItem(R.id.action_block); final MenuItem menuUnblock = menu.findItem(R.id.action_unblock); - if (isConversationsOverviewVisable() - && isConversationsOverviewHideable()) { + if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) { menuArchive.setVisible(false); menuMucDetails.setVisible(false); menuContactDetails.setVisible(false); @@ -309,9 +302,9 @@ public class ConversationActivity extends XmppActivity menuAttach.setVisible(false); menuBlock.setVisible(false); menuUnblock.setVisible(false); + menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite()); } else { menuMucDetails.setVisible(false); - menuInviteContact.setTitle(R.string.conference_with); if (this.getSelectedConversation().isBlocked()) { menuBlock.setVisible(false); } else { diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index aa87f758..c482f268 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -374,10 +374,21 @@ <string name="shared_secret_can_not_be_empty">Dein gemeinsamer Schlüssel darf nicht leer sein</string> <string name="manual_verification_explanation">Vergleiche den angezeigten Fingerabdruck sorgfältig mit dem deines Kontakts.\nDu kannst dazu einen sicheren Kommunikationsweg (z.B. verschlüsselte E-Mail oder Telefonanruf) zum Austausch nutzen.</string> <string name="change_password">Passwort ändern</string> - <string name="current_password">Aktuelles Passwort</string> - <string name="new_password">Neues Passwort</string> - <string name="password_should_not_be_empty">Das Passwort darf nicht leer sein</string> + <string name="current_password">Aktuelles Passwort</string> + <string name="new_password">Neues Passwort</string> + <string name="password_should_not_be_empty">Das Passwort darf nicht leer sein</string> <string name="enable_all_accounts">Alle Konten anschalten</string> - <string name="disable_all_accounts">Alle Konten abschalten</string> + <string name="disable_all_accounts">Alle Konten abschalten</string> <string name="perform_action_with">Aktion durchführen mit</string> + <string name="no_affiliation">Keine Zugehörigkeit</string> + <string name="no_role">Keine Rolle</string> + <string name="outcast">Ausgeschlossen</string> + <string name="member">Mitglied</string> + <string name="advanced_mode">Experten Modus</string> + <string name="grant_membership">Mitgliedschaft gewähren</string> + <string name="remove_membership">Mitgliedschaft entziehen</string> + <string name="grant_admin_privileges">Administratorrechte gewähren</string> + <string name="remove_admin_privileges">Administratorrechte entziehen</string> + <string name="remove_from_room">Aus Konferenz entfernen</string> + <string name="could_not_change_affiliation">Zugehörigkeit kann nicht geändert werden</string> </resources> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index d78e135a..c8e84d75 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -417,4 +417,7 @@ <string name="remove_admin_privileges">Remove admin privileges</string> <string name="remove_from_room">Remove from room</string> <string name="could_not_change_affiliation">Could not change affiliation</string> + <string name="ban_user_from_conference">Ban user from conference</string> + <string name="removing_from_public_conference">You are trying to remove %s from a public conference. The only way to do that is to ban that user for ever.</string> + <string name="ban_now">Ban now</string> </resources> |