diff --git a/broker/migrate/h2/V12__add_group_control_columns.sql b/broker/migrate/h2/V12__add_group_control_columns.sql new file mode 100755 index 00000000..d64d7d17 --- /dev/null +++ b/broker/migrate/h2/V12__add_group_control_columns.sql @@ -0,0 +1,4 @@ +alter table `t_group` add column `_mute` tinyint NOT NULL DEFAULT 0; +alter table `t_group` add column `_join_type` tinyint NOT NULL DEFAULT 0; +alter table `t_group` add column `_private_chat` tinyint NOT NULL DEFAULT 0; +alter table `t_group` add column `_searchable` tinyint NOT NULL DEFAULT 0; diff --git a/broker/migrate/mysql/V12__add_group_control_columns.sql b/broker/migrate/mysql/V12__add_group_control_columns.sql new file mode 100755 index 00000000..d64d7d17 --- /dev/null +++ b/broker/migrate/mysql/V12__add_group_control_columns.sql @@ -0,0 +1,4 @@ +alter table `t_group` add column `_mute` tinyint NOT NULL DEFAULT 0; +alter table `t_group` add column `_join_type` tinyint NOT NULL DEFAULT 0; +alter table `t_group` add column `_private_chat` tinyint NOT NULL DEFAULT 0; +alter table `t_group` add column `_searchable` tinyint NOT NULL DEFAULT 0; diff --git a/broker/src/main/java/io/moquette/imhandler/AddGroupMember.java b/broker/src/main/java/io/moquette/imhandler/AddGroupMember.java index 55aadb33..4f62598f 100644 --- a/broker/src/main/java/io/moquette/imhandler/AddGroupMember.java +++ b/broker/src/main/java/io/moquette/imhandler/AddGroupMember.java @@ -22,7 +22,7 @@ public class AddGroupMember extends GroupHandler 0) { sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), request.getNotifyContent()); diff --git a/broker/src/main/java/io/moquette/imhandler/ModifyGroupInfoHandler.java b/broker/src/main/java/io/moquette/imhandler/ModifyGroupInfoHandler.java index 10ae6462..06411e97 100644 --- a/broker/src/main/java/io/moquette/imhandler/ModifyGroupInfoHandler.java +++ b/broker/src/main/java/io/moquette/imhandler/ModifyGroupInfoHandler.java @@ -14,9 +14,8 @@ import io.moquette.spi.impl.Qos1PublishHandler; import io.netty.buffer.ByteBuf; import cn.wildfirechat.common.ErrorCode; -import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.Modify_Group_Name; -import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.Modify_Group_Portrait; import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS; +import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.*; import static win.liyufan.im.IMTopic.ModifyGroupInfoTopic; @Handler(value = ModifyGroupInfoTopic) @@ -33,6 +32,14 @@ public class ModifyGroupInfoHandler extends GroupHandler { } - if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group && !m_messagesStore.isMemberInGroup(fromUser, message.getConversation().getTarget())) { - return ErrorCode.ERROR_CODE_NOT_IN_GROUP; - } else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group && m_messagesStore.isForbiddenInGroup(fromUser, message.getConversation().getTarget())) { - return ErrorCode.ERROR_CODE_NOT_RIGHT; - } else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_ChatRoom && !m_messagesStore.checkUserClientInChatroom(fromUser, clientID, message.getConversation().getTarget())) { - return ErrorCode.ERROR_CODE_NOT_IN_CHATROOM; - } else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel && !m_messagesStore.checkUserInChannel(fromUser, message.getConversation().getTarget())) { - return ErrorCode.ERROR_CODE_NOT_IN_CHANNEL; + if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group ) { + errorCode = m_messagesStore.canSendMessageInGroup(fromUser, message.getConversation().getTarget()); + if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) { + return errorCode; + } + } else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_ChatRoom) { + if(!m_messagesStore.checkUserClientInChatroom(fromUser, clientID, message.getConversation().getTarget())) { + return ErrorCode.ERROR_CODE_NOT_IN_CHATROOM; + } + } else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel) { + if(!m_messagesStore.checkUserInChannel(fromUser, message.getConversation().getTarget())) { + return ErrorCode.ERROR_CODE_NOT_IN_CHANNEL; + } } } diff --git a/broker/src/main/java/io/moquette/persistence/DatabaseStore.java b/broker/src/main/java/io/moquette/persistence/DatabaseStore.java index f808c6a3..07ce400d 100644 --- a/broker/src/main/java/io/moquette/persistence/DatabaseStore.java +++ b/broker/src/main/java/io/moquette/persistence/DatabaseStore.java @@ -793,7 +793,11 @@ public class DatabaseStore { ", `_extra`" + ", `_dt`" + ", `_member_count`" + - ", `_member_dt`) values(?, ?, ?, ?, ?, ?, ?, ?, ?)" + + ", `_mute`" + + ", `_join_type`" + + ", `_private_chat`" + + ", `_searchable`" + + ", `_member_dt`) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + " ON DUPLICATE KEY UPDATE " + "`_name` = ?," + "`_portrait` = ?," + @@ -802,6 +806,10 @@ public class DatabaseStore { "`_extra` = ?," + "`_dt` = ?," + "`_member_count` = ?," + + "`_mute` = ?" + + ", `_join_type` = ?" + + ", `_private_chat` = ?" + + ", `_searchable` = ?, " + "`_member_dt` = ?"; @@ -816,6 +824,10 @@ public class DatabaseStore { statement.setString(index++, groupInfo.getExtra()); statement.setLong(index++, groupInfo.getUpdateDt() == 0 ? System.currentTimeMillis() : groupInfo.getUpdateDt()); statement.setInt(index++, groupInfo.getMemberCount()); + statement.setInt(index++, groupInfo.getMute()); + statement.setInt(index++, groupInfo.getJoinType()); + statement.setInt(index++, groupInfo.getPrivateChat()); + statement.setInt(index++, groupInfo.getSearchable()); statement.setLong(index++, groupInfo.getMemberUpdateDt() == 0 ? System.currentTimeMillis() : groupInfo.getUpdateDt()); statement.setString(index++, groupInfo.getName()); @@ -825,6 +837,10 @@ public class DatabaseStore { statement.setString(index++, groupInfo.getExtra()); statement.setLong(index++, groupInfo.getUpdateDt() == 0 ? System.currentTimeMillis() : groupInfo.getUpdateDt()); statement.setInt(index++, groupInfo.getMemberCount()); + statement.setInt(index++, groupInfo.getMute()); + statement.setInt(index++, groupInfo.getJoinType()); + statement.setInt(index++, groupInfo.getPrivateChat()); + statement.setInt(index++, groupInfo.getSearchable()); statement.setLong(index++, groupInfo.getMemberUpdateDt() == 0 ? System.currentTimeMillis() : groupInfo.getUpdateDt()); int count = statement.executeUpdate(); LOG.info("Update rows {}", count); @@ -1155,6 +1171,10 @@ public class DatabaseStore { ", `_dt`" + ", `_member_count`" + ", `_member_dt`" + + ", `_mute`" + + ", `_join_type`" + + ", `_private_chat`" + + ", `_searchable`" + " from t_group where `_gid` = ?"; statement = connection.prepareStatement(sql); @@ -1190,18 +1210,27 @@ public class DatabaseStore { strValue = (strValue == null ? "" : strValue); builder.setExtra(strValue); - - long longValue = rs.getLong(index++); builder.setUpdateDt(longValue); - intValue = rs.getInt(index++); builder.setMemberCount(intValue); longValue = rs.getLong(index++); builder.setMemberUpdateDt(longValue); + intValue = rs.getInt(index++); + builder.setMute(intValue); + + intValue = rs.getInt(index++); + builder.setJoinType(intValue); + + intValue = rs.getInt(index++); + builder.setPrivateChat(intValue); + + intValue = rs.getInt(index++); + builder.setSearchable(intValue); + return builder.build(); } } catch (SQLException e) { diff --git a/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java b/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java index bf5b4690..7012a590 100755 --- a/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java +++ b/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java @@ -49,10 +49,9 @@ import java.util.function.Consumer; import static cn.wildfirechat.proto.ProtoConstants.ChannelStatus.Channel_Status_Destoryed; +import static cn.wildfirechat.proto.ProtoConstants.GroupMemberType.*; import static cn.wildfirechat.proto.ProtoConstants.ModifyChannelInfoType.*; -import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.Modify_Group_Extra; -import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.Modify_Group_Name; -import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.Modify_Group_Portrait; +import static cn.wildfirechat.proto.ProtoConstants.ModifyGroupInfoType.*; import static cn.wildfirechat.proto.ProtoConstants.PersistFlag.Transparent; import static io.moquette.BrokerConstants.*; import static io.moquette.server.Constants.MAX_CHATROOM_MESSAGE_QUEUE; @@ -197,7 +196,7 @@ public class MemoryMessagesStore implements IMessagesStore { MultiMap groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS); Collection members = groupMembers.get(message.getConversation().getTarget()); for (WFCMessage.GroupMember member : members) { - if (member.getType() != ProtoConstants.GroupMemberType.GroupMemberType_Removed) { + if (member.getType() != GroupMemberType_Removed) { notifyReceivers.add(member.getMemberId()); } } @@ -603,19 +602,25 @@ public class MemoryMessagesStore implements IMessagesStore { mIMap.put(groupId, groupInfo); MultiMap groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS); + List updatedMemberList = new ArrayList<>(); for (WFCMessage.GroupMember member : memberList) { - member = member.toBuilder().setUpdateDt(dt).build(); + if (member.getMemberId().equals(groupInfo.getOwner())) { + member = member.toBuilder().setUpdateDt(dt).setType(ProtoConstants.GroupMemberType.GroupMemberType_Owner).build(); + } else { + member = member.toBuilder().setUpdateDt(dt).build(); + } groupMembers.put(groupId, member); + updatedMemberList.add(member); } - databaseStore.persistGroupMember(groupId, memberList); + databaseStore.persistGroupMember(groupId, updatedMemberList); return groupInfo; } @Override - public ErrorCode addGroupMembers(String operator, String groupId, List memberList) { + public ErrorCode addGroupMembers(String operator, boolean isAdmin, String groupId, List memberList) { HazelcastInstance hzInstance = m_Server.getHazelcastInstance(); IMap mIMap = hzInstance.getMap(GROUPS_MAP); @@ -629,6 +634,22 @@ public class MemoryMessagesStore implements IMessagesStore { MultiMap groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS); + if (!isAdmin) { + boolean isMember = false; + for (WFCMessage.GroupMember member : memberList) { + if (member.getType() == GroupMemberType_Removed) { + return ErrorCode.ERROR_CODE_NOT_IN_GROUP; + } else if(member.getType() == GroupMemberType_Silent) { + return ErrorCode.ERROR_CODE_NOT_RIGHT; + } + isMember = true; + break; + } + if (!isMember) { + return ErrorCode.ERROR_CODE_NOT_IN_GROUP; + } + } + long updateDt = System.currentTimeMillis(); List tmp = new ArrayList<>(); @@ -652,7 +673,7 @@ public class MemoryMessagesStore implements IMessagesStore { int count = 0; for (WFCMessage.GroupMember member : groupMembers.get(groupId)) { - if (member.getType() != ProtoConstants.GroupMemberType.GroupMemberType_Removed) { + if (member.getType() != GroupMemberType_Removed) { count++; } } @@ -688,7 +709,7 @@ public class MemoryMessagesStore implements IMessagesStore { boolean removed = groupMembers.remove(groupId, member); if (removed) { removeCount++; - member = member.toBuilder().setType(ProtoConstants.GroupMemberType.GroupMemberType_Removed).setUpdateDt(updateDt).build(); + member = member.toBuilder().setType(GroupMemberType_Removed).setUpdateDt(updateDt).build(); groupMembers.put(groupId, member); list.add(member); } @@ -739,7 +760,7 @@ public class MemoryMessagesStore implements IMessagesStore { databaseStore.persistGroupMember(groupId, list); databaseStore.updateGroupMemberCountDt(groupId, groupInfo.getMemberCount()-1, updateDt); - member = member.toBuilder().setType(ProtoConstants.GroupMemberType.GroupMemberType_Removed).setUpdateDt(updateDt).build(); + member = member.toBuilder().setType(GroupMemberType_Removed).setUpdateDt(updateDt).build(); groupMembers.put(groupId, member); } break; @@ -818,6 +839,16 @@ public class MemoryMessagesStore implements IMessagesStore { newInfoBuilder.setPortrait(value); else if(modifyType == Modify_Group_Extra) newInfoBuilder.setExtra(value); + else if(modifyType == Modify_Group_Mute) + newInfoBuilder.setMute(Integer.parseInt(value)); + else if(modifyType == Modify_Group_JoinType) + newInfoBuilder.setJoinType(Integer.parseInt(value)); + else if(modifyType == Modify_Group_PrivateChat) + newInfoBuilder.setPrivateChat(Integer.parseInt(value)); + else if(modifyType == Modify_Group_Searchable) + newInfoBuilder.setSearchable(Integer.parseInt(value)); + else + return ErrorCode.INVALID_PARAMETER; newInfoBuilder.setUpdateDt(System.currentTimeMillis()); @@ -951,7 +982,7 @@ public class MemoryMessagesStore implements IMessagesStore { for (WFCMessage.GroupMember member : members ) { - if (member.getMemberId().equals(memberId) && member.getType() != ProtoConstants.GroupMemberType.GroupMemberType_Removed) + if (member.getMemberId().equals(memberId) && member.getType() != GroupMemberType_Removed) return true; } @@ -959,19 +990,37 @@ public class MemoryMessagesStore implements IMessagesStore { } @Override - public boolean isForbiddenInGroup(String memberId, String groupId) { + public ErrorCode canSendMessageInGroup(String memberId, String groupId) { HazelcastInstance hzInstance = m_Server.getHazelcastInstance(); MultiMap groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS); + IMap groups = hzInstance.getMap(GROUPS_MAP); + WFCMessage.GroupInfo groupInfo = groups.get(groupId); + boolean isMute = false; + if (groupInfo != null) { + isMute = groupInfo.getMute()>0; + } Collection members = groupMembers.get(groupId); for (WFCMessage.GroupMember member : members ) { - if (member.getMemberId().equals(memberId) && member.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Silent) - return true; + if (member.getMemberId().equals(memberId)) { + if (member.getType() == GroupMemberType_Silent) { + return ErrorCode.ERROR_CODE_NOT_RIGHT; + } + + if (isMute && member.getType() != GroupMemberType_Manager && member.getType() != GroupMemberType_Owner) { + return ErrorCode.ERROR_CODE_GROUP_MUTED; + } + + if (member.getMemberId().equals(memberId) && member.getType() != GroupMemberType_Removed) { + return ErrorCode.ERROR_CODE_NOT_IN_GROUP; + } + } + } - return false; + return ErrorCode.ERROR_CODE_SUCCESS; } @Override @@ -1002,7 +1051,7 @@ public class MemoryMessagesStore implements IMessagesStore { Collection members = groupMembers.get(message.getConversation().getTarget()); for (WFCMessage.GroupMember member : members) { if (member.getMemberId().equals(operatorId)) { - if (member.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Manager || member.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Owner) { + if (member.getType() == GroupMemberType_Manager || member.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Owner) { canRecall = true; } break; diff --git a/broker/src/main/java/io/moquette/spi/IMessagesStore.java b/broker/src/main/java/io/moquette/spi/IMessagesStore.java index c91b2cb4..88f8717d 100755 --- a/broker/src/main/java/io/moquette/spi/IMessagesStore.java +++ b/broker/src/main/java/io/moquette/spi/IMessagesStore.java @@ -103,7 +103,7 @@ public interface IMessagesStore { WFCMessage.PullMessageResult loadRemoteMessages(String user, WFCMessage.Conversation conversation, long beforeUid, int count); long insertUserMessages(String sender, int conversationType, String target, int line, int messageContentType, String userId, long messageId); WFCMessage.GroupInfo createGroup(String operator, WFCMessage.GroupInfo groupInfo, List memberList); - ErrorCode addGroupMembers(String operator, String groupId, List memberList); + ErrorCode addGroupMembers(String operator, boolean isAdmin, String groupId, List memberList); ErrorCode kickoffGroupMembers(String operator, String groupId, List memberList); ErrorCode quitGroup(String operator, String groupId); ErrorCode dismissGroup(String operator, String groupId, boolean isAdmin); @@ -114,7 +114,7 @@ public interface IMessagesStore { ErrorCode getGroupMembers(String groupId, long maxDt, List members); ErrorCode transferGroup(String operator, String groupId, String newOwner, boolean isAdmin); boolean isMemberInGroup(String member, String groupId); - boolean isForbiddenInGroup(String member, String groupId); + ErrorCode canSendMessageInGroup(String member, String groupId); ErrorCode recallMessage(long messageUid, String operatorId); diff --git a/common/src/main/java/cn.wildfirechat.pojos/GroupNotificationBinaryContent.java b/common/src/main/java/cn.wildfirechat.pojos/GroupNotificationBinaryContent.java index dc5bbbef..073a7d70 100644 --- a/common/src/main/java/cn.wildfirechat.pojos/GroupNotificationBinaryContent.java +++ b/common/src/main/java/cn.wildfirechat.pojos/GroupNotificationBinaryContent.java @@ -22,13 +22,13 @@ public class GroupNotificationBinaryContent { //operator private String o; - //name + //value1(name or something) private String n; //members private List ms; - //members + //value2(member or something) private String m; public GroupNotificationBinaryContent(String g, String o, String n, String m) { @@ -124,4 +124,21 @@ public class GroupNotificationBinaryContent { public WFCMessage.MessageContent getModifyGroupMemberAliasNotifyContent() { return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_MODIFY_GROUP_ALIAS); } + + public WFCMessage.MessageContent getChangeGroupMuteNotifyContent() { + return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_CHANGE_MUTE); + } + + public WFCMessage.MessageContent getChangeGroupJointypeNotifyContent() { + return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_CHANGE_JOINTYPE); + } + + public WFCMessage.MessageContent getChangeGroupPrivatechatNotifyContent() { + return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_CHANGE_PRIVATECHAT); + } + + public WFCMessage.MessageContent getChangeGroupSearchableNotifyContent() { + return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_CHANGE_SEARCHABLE); + } + } diff --git a/common/src/main/java/cn/wildfirechat/common/ErrorCode.java b/common/src/main/java/cn/wildfirechat/common/ErrorCode.java index a5f78484..9fb4cf0f 100755 --- a/common/src/main/java/cn/wildfirechat/common/ErrorCode.java +++ b/common/src/main/java/cn/wildfirechat/common/ErrorCode.java @@ -42,6 +42,7 @@ public enum ErrorCode { ERROR_CODE_NOT_IN_CHANNEL(21, "not in channel"), + ERROR_CODE_GROUP_MUTED(241, "group is muted"), ERROR_CODE_SENSITIVE_MATCHED(242, "sensitive matched"), ERROR_CODE_SIGN_EXPIRED(243, "sign expired"), ERROR_CODE_AUTH_FAILURE(244, "auth failure"), diff --git a/common/src/main/java/cn/wildfirechat/proto/ProtoConstants.java b/common/src/main/java/cn/wildfirechat/proto/ProtoConstants.java index 22231e90..3a20caed 100644 --- a/common/src/main/java/cn/wildfirechat/proto/ProtoConstants.java +++ b/common/src/main/java/cn/wildfirechat/proto/ProtoConstants.java @@ -141,5 +141,10 @@ public class ProtoConstants { public static final int MESSAGE_CONTENT_TYPE_CHANGE_GROUP_NAME = 110; public static final int MESSAGE_CONTENT_TYPE_MODIFY_GROUP_ALIAS = 111; public static final int MESSAGE_CONTENT_TYPE_CHANGE_GROUP_PORTRAIT = 112; + + public static final int MESSAGE_CONTENT_TYPE_CHANGE_MUTE = 113; + public static final int MESSAGE_CONTENT_TYPE_CHANGE_JOINTYPE = 114; + public static final int MESSAGE_CONTENT_TYPE_CHANGE_PRIVATECHAT = 115; + public static final int MESSAGE_CONTENT_TYPE_CHANGE_SEARCHABLE = 116; }