From 800f87af07e7ecca3b4b8ee7007cf27f1a87dbd3 Mon Sep 17 00:00:00 2001 From: RoyceDa Date: Mon, 23 Feb 2026 13:40:20 +0200 Subject: [PATCH 1/4] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=83=D1=8F=D0=B7=D0=B2=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D1=87=D1=83=D0=B6=D0=BE=D0=B3=D0=BE=20?= =?UTF-8?q?=D1=87=D1=82=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/im/rosetta/executors/Executor7Read.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/im/rosetta/executors/Executor7Read.java b/src/main/java/im/rosetta/executors/Executor7Read.java index 8383e21..56aa2e1 100644 --- a/src/main/java/im/rosetta/executors/Executor7Read.java +++ b/src/main/java/im/rosetta/executors/Executor7Read.java @@ -38,6 +38,14 @@ public class Executor7Read extends PacketExecutor { client.disconnect(Failures.HANDSHAKE_NOT_COMPLETED); return; } + if(!eciAuthentificate.getPublicKey().equals(fromPublicKey)){ + /** + * Клиент пытается прочитать сообщения от имени того, кем не является + */ + client.disconnect(Failures.DATA_MISSMATCH); + return; + } + packet.setPrivateKey(""); if(toPublicKey.startsWith("#group:")){ -- 2.49.1 From b0f0986e0d8dee121b8d21cef298ecb2f43c81bc Mon Sep 17 00:00:00 2001 From: RoyceDa Date: Mon, 23 Feb 2026 13:50:26 +0200 Subject: [PATCH 2/4] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BD=D0=B5=D0=B2=D0=B5=D1=80=D0=BD=D0=BE?= =?UTF-8?q?=D0=B5=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20IP=20=D0=B0=D0=B4=D1=80=D0=B5=D1=81=D0=B0=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20=D1=83=D1=81=D1=82=D1=80=D0=BE=D0=B9=D1=81=D1=82=D0=B2=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/im/rosetta/executors/Executor0Handshake.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/im/rosetta/executors/Executor0Handshake.java b/src/main/java/im/rosetta/executors/Executor0Handshake.java index 9c3e27d..727cffd 100644 --- a/src/main/java/im/rosetta/executors/Executor0Handshake.java +++ b/src/main/java/im/rosetta/executors/Executor0Handshake.java @@ -184,7 +184,7 @@ public class Executor0Handshake extends PacketExecutor { newDevicePacket.setDeviceId(deviceId); newDevicePacket.setDeviceName(deviceName); newDevicePacket.setDeviceOs(deviceOs); - newDevicePacket.setIpAddress(client.getSocket().getRemoteSocketAddress().getAddress().getHostAddress()); + newDevicePacket.setIpAddress(client.getIpAddress()); clientManager.sendPacketToAuthorizedPK(publicKey, newDevicePacket); /** * Сбрасываем клиенту все старые подтверждения устройств, чтобы исключить спам запросами -- 2.49.1 From 773659c2bac5af59f14a1db9b16fab5648740b81 Mon Sep 17 00:00:00 2001 From: RoyceDa Date: Mon, 23 Feb 2026 17:30:15 +0200 Subject: [PATCH 3/4] =?UTF-8?q?=D0=A1=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B4=D0=BB=D1=8F=20=D0=BE?= =?UTF-8?q?=D1=84=D0=BB=D0=B0=D0=B9=D0=BD=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B2=20?= =?UTF-8?q?=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../im/rosetta/database/entity/Group.java | 30 +++-- .../database/repository/GroupRepository.java | 109 +++++++++++++----- .../service/dispatch/MessageDispatcher.java | 17 ++- .../service/services/BufferService.java | 20 +++- 4 files changed, 133 insertions(+), 43 deletions(-) diff --git a/src/main/java/im/rosetta/database/entity/Group.java b/src/main/java/im/rosetta/database/entity/Group.java index 5117702..6486638 100644 --- a/src/main/java/im/rosetta/database/entity/Group.java +++ b/src/main/java/im/rosetta/database/entity/Group.java @@ -3,15 +3,20 @@ package im.rosetta.database.entity; import java.util.ArrayList; import java.util.List; -import im.rosetta.database.CreateUpdateEntity; -import im.rosetta.database.converters.StringListConverter; +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import im.rosetta.database.CreateUpdateEntity; +import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; -import jakarta.persistence.Convert; +import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OrderColumn; import jakarta.persistence.Table; /** @@ -28,13 +33,20 @@ public class Group extends CreateUpdateEntity { @Column(name = "groupId") private String groupId; - @Convert(converter = StringListConverter.class) - @Column(name = "membersPublicKeys", nullable = false, columnDefinition = "TEXT") - private List membersPublicKeys = new ArrayList<>(); - @Convert(converter = StringListConverter.class) - @Column(name = "bannedPublicKeys", nullable = false, columnDefinition = "TEXT") - private List bannedPublicKeys = new ArrayList<>(); + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "group_members", joinColumns = @JoinColumn(name = "group_id")) + @Column(name = "public_key", nullable = false, columnDefinition = "TEXT") + @OrderColumn(name = "order_index") + @Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN }) + private List membersPublicKeys = new ArrayList<>(); + + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "group_banned", joinColumns = @JoinColumn(name = "group_id")) + @Column(name = "public_key", nullable = false, columnDefinition = "TEXT") + @OrderColumn(name = "order_index") + @Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN }) + private List bannedPublicKeys = new ArrayList<>(); public Long getId() { return id; diff --git a/src/main/java/im/rosetta/database/repository/GroupRepository.java b/src/main/java/im/rosetta/database/repository/GroupRepository.java index 130d469..3de8f56 100644 --- a/src/main/java/im/rosetta/database/repository/GroupRepository.java +++ b/src/main/java/im/rosetta/database/repository/GroupRepository.java @@ -1,8 +1,12 @@ package im.rosetta.database.repository; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import org.hibernate.Hibernate; + +import im.rosetta.database.QuerySession; import im.rosetta.database.Repository; import im.rosetta.database.entity.Group; @@ -18,11 +22,16 @@ public class GroupRepository extends Repository { * @return список публичных ключей участников группы */ public List findGroupMembers(String groupId) { - Group group = this.findByField("groupId", groupId); - if(group == null) { + try(QuerySession querySession = this.buildQuery("FROM Group g WHERE g.groupId = :groupId", new HashMap(){{ + put("groupId", groupId); + }})){ + Group group = querySession.getQuery().uniqueResult(); + if(group != null) { + Hibernate.initialize(group.getMembersPublicKeys()); + return group.getMembersPublicKeys(); + } return new ArrayList<>(); } - return group.getMembersPublicKeys(); } /** @@ -45,7 +54,18 @@ public class GroupRepository extends Repository { * @return группа с заданным id, или null, если группа не найдена */ public Group getGroup(String groupId) { - return this.findByField("groupId", groupId); + String hql = "FROM Group g WHERE g.groupId = :groupId"; + HashMap params = new HashMap<>(); + params.put("groupId", groupId); + + try (QuerySession qs = this.buildQuery(hql, params)) { + Group group = qs.getQuery().uniqueResult(); + if (group != null) { + Hibernate.initialize(group.getMembersPublicKeys()); + Hibernate.initialize(group.getBannedPublicKeys()); + } + return group; + } } /** @@ -65,13 +85,18 @@ public class GroupRepository extends Repository { * @param memberPublicKey публичный ключ участника, которого нужно добавить в группу */ public void addMemberToGroup(String groupId, String memberPublicKey) { - Group group = this.findByField("groupId", groupId); - if(group != null) { - List membersPublicKeys = group.getMembersPublicKeys(); - if(!membersPublicKeys.contains(memberPublicKey)) { - membersPublicKeys.add(memberPublicKey); - group.setMembersPublicKeys(membersPublicKeys); - this.update(group); + try(QuerySession querySession = this.buildQuery("FROM Group g WHERE g.groupId = :groupId", new HashMap(){{ + put("groupId", groupId); + }})){ + Group group = querySession.getQuery().uniqueResult(); + if(group != null) { + Hibernate.initialize(group.getMembersPublicKeys()); + List membersPublicKeys = group.getMembersPublicKeys(); + if(!membersPublicKeys.contains(memberPublicKey)) { + membersPublicKeys.add(memberPublicKey); + group.setMembersPublicKeys(membersPublicKeys); + this.update(group); + } } } } @@ -82,13 +107,18 @@ public class GroupRepository extends Repository { * @param memberPublicKey публичный ключ участника, которого нужно удалить из группы */ public void removeMemberFromGroup(String groupId, String memberPublicKey) { - Group group = this.findByField("groupId", groupId); - if(group != null) { - List membersPublicKeys = group.getMembersPublicKeys(); - if(membersPublicKeys.contains(memberPublicKey)) { - membersPublicKeys.remove(memberPublicKey); - group.setMembersPublicKeys(membersPublicKeys); - this.update(group); + try(QuerySession querySession = this.buildQuery("FROM Group g WHERE g.groupId = :groupId", new HashMap(){{ + put("groupId", groupId); + }})){ + Group group = querySession.getQuery().uniqueResult(); + if(group != null) { + Hibernate.initialize(group.getMembersPublicKeys()); + List membersPublicKeys = group.getMembersPublicKeys(); + if(membersPublicKeys.contains(memberPublicKey)) { + membersPublicKeys.remove(memberPublicKey); + group.setMembersPublicKeys(membersPublicKeys); + this.update(group); + } } } } @@ -99,20 +129,39 @@ public class GroupRepository extends Repository { * @param memberPublicKey публичный ключ участника, которого нужно забанить в группе */ public void banMemberInGroup(String groupId, String memberPublicKey) { - Group group = this.findByField("groupId", groupId); - if(group != null) { - List bannedPublicKeys = group.getBannedPublicKeys(); - List membersPublicKeys = group.getMembersPublicKeys(); - if(membersPublicKeys.contains(memberPublicKey)) { - membersPublicKeys.remove(memberPublicKey); - group.setMembersPublicKeys(membersPublicKeys); + try(QuerySession querySession = this.buildQuery("FROM Group g WHERE g.groupId = :groupId", new HashMap(){{ + put("groupId", groupId); + }})){ + Group group = querySession.getQuery().uniqueResult(); + if(group != null) { + Hibernate.initialize(group.getBannedPublicKeys()); + List bannedPublicKeys = group.getBannedPublicKeys(); + if(!bannedPublicKeys.contains(memberPublicKey)) { + bannedPublicKeys.add(memberPublicKey); + group.setBannedPublicKeys(bannedPublicKeys); + this.update(group); + } } - if(!bannedPublicKeys.contains(memberPublicKey)) { - bannedPublicKeys.add(memberPublicKey); - group.setBannedPublicKeys(bannedPublicKeys); - } - this.update(group); } } + /** + * Получает все группы в которых состоит пользователь с заданным публичным ключом + * @param memberPublicKey публичный ключ пользователя, для которого нужно найти группы + * @return список ID групп, в которых состоит пользователь с заданным публичным ключом, или пустой список, если таких групп нет + */ + public List findGroupsByMember(String memberPublicKey) { + String hql = "FROM Group g WHERE :memberPublicKey MEMBER OF g.membersPublicKeys"; + List groupIds = new ArrayList<>(); + HashMap parameters = new HashMap<>(); + parameters.put("memberPublicKey", memberPublicKey); + try(QuerySession querySession = this.buildQuery(hql, parameters)){ + List groups = querySession.getQuery().list(); + for(Group group : groups){ + groupIds.add(group.getGroupId()); + } + } + return groupIds; + } + } diff --git a/src/main/java/im/rosetta/service/dispatch/MessageDispatcher.java b/src/main/java/im/rosetta/service/dispatch/MessageDispatcher.java index 3636ad7..0644ebd 100644 --- a/src/main/java/im/rosetta/service/dispatch/MessageDispatcher.java +++ b/src/main/java/im/rosetta/service/dispatch/MessageDispatcher.java @@ -7,6 +7,7 @@ import im.rosetta.client.ClientManager; import im.rosetta.client.tags.ECIAuthentificate; import im.rosetta.database.repository.BufferRepository; import im.rosetta.database.repository.GroupRepository; +import im.rosetta.packet.Packet11Typeing; import im.rosetta.packet.base.PacketBaseDialog; import im.rosetta.service.services.BufferService; @@ -19,7 +20,7 @@ import io.orprotocol.packet.PacketManager; * Такой диспетчер нужен для того, чтобы не загромождать логику обработчиков сообщений, а так же для того, чтобы * централизовать логику отправки сообщений и сохранения их в буфер * Например, при отправке группового сообщения, диспетчер сам достает участников группы и - * отправляет сообщение каждому участнику, а так же сохраняет сообщение в буфер для каждого участника, который офлайн + * отправляет сообщение каждому участнику */ public class MessageDispatcher { @@ -38,6 +39,7 @@ public class MessageDispatcher { * @param packet пакет с групповым сообщением */ public void sendGroup(PacketBaseDialog packet, Client client, ECIAuthentificate eciAuthentificate) throws ProtocolException { + String fromPublicKey = packet.getFromPublicKey(); String toPublicKey = packet.getToPublicKey(); List groupMembersPublicKeys = this.groupRepository.findGroupMembers(toPublicKey.replace("#group:", "")); if(groupMembersPublicKeys.isEmpty()){ @@ -67,7 +69,18 @@ public class MessageDispatcher { } this.clientManager.sendPacketToAuthorizedPK(groupMembersPublicKeys, packet); - //TODO: Сохранить сообщение в буфер для группы, чтобы группы тоже синхронизировались + if(packet instanceof Packet11Typeing){ + /** + * Если это пакет печати его не обязательно кэшировать, так как он нужен только + * для отображения статуса печати в реальном времени + */ + return; + } + /** + * Кладем пакет в буфер для будущей синхронизации и на случай если кто-то из участников оффлайн, + * в toPublicKey при отправке в группу у нас находится #group:groupId + */ + this.bufferService.pushPacketToBuffer(fromPublicKey, toPublicKey.replace("#group:", ""), packet); } /** diff --git a/src/main/java/im/rosetta/service/services/BufferService.java b/src/main/java/im/rosetta/service/services/BufferService.java index a179dd4..2d04fca 100644 --- a/src/main/java/im/rosetta/service/services/BufferService.java +++ b/src/main/java/im/rosetta/service/services/BufferService.java @@ -8,6 +8,7 @@ import im.rosetta.client.tags.ECIAuthentificate; import im.rosetta.database.QuerySession; import im.rosetta.database.entity.Buffer; import im.rosetta.database.repository.BufferRepository; +import im.rosetta.database.repository.GroupRepository; import im.rosetta.exception.UnauthorizedExeception; import im.rosetta.packet.Packet7Read; import im.rosetta.service.Service; @@ -20,6 +21,7 @@ import io.orprotocol.packet.PacketManager; public class BufferService extends Service { private PacketManager packetManager; + private GroupRepository groupRepository = new GroupRepository(); public BufferService(BufferRepository repository, PacketManager packetManager) { super(repository); @@ -41,10 +43,24 @@ public class BufferService extends Service { */ throw new UnauthorizedExeception("Unauthorized client cannot get packets from buffer"); } + /** + * Получаем группы клиента, и исходя из этого формируем список каких пакетов нам нужно взять из базы + */ + List clientGroups = this.groupRepository.findGroupsByMember(eciAuthentificate.getPublicKey()); + List toValue = new ArrayList<>(); + /** + * Добавляем публичный ключ клиента, так как ему нужны пакеты, которые были отправлены ему напрямую, а не в группу + */ + toValue.add(eciAuthentificate.getPublicKey()); + /** + * Добавляем группы клиента, так как ему нужны пакеты, которые были отправлены в эти группы + */ + toValue.addAll(clientGroups); + String toPublicKey = eciAuthentificate.getPublicKey(); - String hql = "FROM Buffer WHERE (to = :to OR from = :from) AND timestamp > :timestamp ORDER BY timestamp ASC"; + String hql = "FROM Buffer WHERE (to IN (:to) OR from = :from) AND timestamp > :timestamp ORDER BY timestamp ASC"; HashMap parameters = new HashMap<>(); - parameters.put("to", toPublicKey); + parameters.put("to", toValue); parameters.put("from", toPublicKey); parameters.put("timestamp", fromTimestampMs); List packets = new ArrayList<>(); -- 2.49.1 From 0e240d1eeb637b2cc03d50cbd3571d6f06a9235f Mon Sep 17 00:00:00 2001 From: RoyceDa Date: Tue, 24 Feb 2026 18:15:22 +0200 Subject: [PATCH 4/4] =?UTF-8?q?=D0=A1=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D0=B9,=20=D1=87=D1=82=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D0=B0?= =?UTF-8?q?=D1=85,=20=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BA=D0=BB=D1=8E=D1=87=D0=B5?= =?UTF-8?q?=D0=B9=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/im/rosetta/Boot.java | 2 +- .../java/im/rosetta/client/ClientManager.java | 30 +++++++- .../database/repository/GroupRepository.java | 5 +- .../executors/Executor17GroupCreate.java | 2 +- .../executors/Executor20GroupJoin.java | 23 +++++- .../im/rosetta/packet/Packet20GroupJoin.java | 26 +++++++ .../service/dispatch/MessageDispatcher.java | 72 ++++++------------- 7 files changed, 101 insertions(+), 59 deletions(-) diff --git a/src/main/java/im/rosetta/Boot.java b/src/main/java/im/rosetta/Boot.java index 4cbdfcc..2e339e5 100644 --- a/src/main/java/im/rosetta/Boot.java +++ b/src/main/java/im/rosetta/Boot.java @@ -200,7 +200,7 @@ public class Boot { this.packetManager.registerExecutor(17, new Executor17GroupCreate()); this.packetManager.registerExecutor(18, new Executor18GroupInfo()); this.packetManager.registerExecutor(19, new Executor19GroupInviteInfo()); - this.packetManager.registerExecutor(20, new Executor20GroupJoin()); + this.packetManager.registerExecutor(20, new Executor20GroupJoin(this.packetManager)); this.packetManager.registerExecutor(21, new Executor21GroupLeave()); this.packetManager.registerExecutor(22, new Executor22GroupBan()); this.packetManager.registerExecutor(24, new Executor24DeviceResolve(this.clientManager, this.eventManager, this.packetManager)); diff --git a/src/main/java/im/rosetta/client/ClientManager.java b/src/main/java/im/rosetta/client/ClientManager.java index b4c8112..0ec7650 100644 --- a/src/main/java/im/rosetta/client/ClientManager.java +++ b/src/main/java/im/rosetta/client/ClientManager.java @@ -53,7 +53,7 @@ public class ClientManager { } /** - * Отправить пакет всем АВТОРИЗОВАННЫМ клиентам с публичным ключом publicKey + * Отправить пакет ВСЕМ АВТОРИЗОВАННЫМ клиентам с публичным ключом publicKey * @param publicKey публичный ключ получателя * @param packet пакет для отправки * @throws ProtocolException если произошла ошибка при отправке пакета клиенту @@ -83,6 +83,34 @@ public class ClientManager { } } + /** + * Отправить пакет всем клиентам с публичными ключом как у client, кроме клиента client, который является отправителем и не должен получать этот пакет + * @param client клиент + * @param packet пакет для отправки + * @throws ProtocolException + */ + public void retranslate(Client client, Packet packet) throws ProtocolException{ + ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class); + HashSet clients = this.clientIndexer + .getClients(ECIAuthentificate.class, "publicKey", eciAuthentificate.getPublicKey()); + if(clients == null){ + /** + * Нет авторизованных сессий с таким публичным ключом + */ + return; + } + for(Client c : clients){ + /** + * Проходим по всем устройствам с таким публичным ключом и ретранслируем им пакет, кроме того устройства что + * отправило пакет + */ + if(c.equals(client)){ + continue; + } + c.send(packet); + } + } + /** * Отправить пакет всем клиентам с публичными ключами из списка publicKeys * @param publicKeys список публичных ключей получателей diff --git a/src/main/java/im/rosetta/database/repository/GroupRepository.java b/src/main/java/im/rosetta/database/repository/GroupRepository.java index 3de8f56..2173234 100644 --- a/src/main/java/im/rosetta/database/repository/GroupRepository.java +++ b/src/main/java/im/rosetta/database/repository/GroupRepository.java @@ -39,12 +39,9 @@ public class GroupRepository extends Repository { * @param groupId ID группы * @param creatorPublicKey публичный ключ создателя группы, который будет единственным участником группы при создании */ - public void createGroup(String groupId, String creatorPublicKey) { + public void createGroup(String groupId) { Group group = new Group(); group.setGroupId(groupId); - List membersPublicKeys = new ArrayList<>(); - membersPublicKeys.add(creatorPublicKey); - group.setMembersPublicKeys(membersPublicKeys); this.save(group); } diff --git a/src/main/java/im/rosetta/executors/Executor17GroupCreate.java b/src/main/java/im/rosetta/executors/Executor17GroupCreate.java index e85dfae..23de798 100644 --- a/src/main/java/im/rosetta/executors/Executor17GroupCreate.java +++ b/src/main/java/im/rosetta/executors/Executor17GroupCreate.java @@ -25,7 +25,7 @@ public class Executor17GroupCreate extends PacketExecutor { return; } String groupId = RandomUtil.randomString(16); - this.groupRepository.createGroup(groupId, eciAuthentificate.getPublicKey()); + this.groupRepository.createGroup(groupId); /** * Отправляем клиенту ид созданной группы */ diff --git a/src/main/java/im/rosetta/executors/Executor20GroupJoin.java b/src/main/java/im/rosetta/executors/Executor20GroupJoin.java index 6f89877..b258bda 100644 --- a/src/main/java/im/rosetta/executors/Executor20GroupJoin.java +++ b/src/main/java/im/rosetta/executors/Executor20GroupJoin.java @@ -3,17 +3,25 @@ package im.rosetta.executors; import im.rosetta.Failures; import im.rosetta.client.tags.ECIAuthentificate; import im.rosetta.database.entity.Group; +import im.rosetta.database.repository.BufferRepository; import im.rosetta.database.repository.GroupRepository; import im.rosetta.packet.Packet20GroupJoin; import im.rosetta.packet.runtime.NetworkGroupStatus; - +import im.rosetta.service.services.BufferService; import io.orprotocol.ProtocolException; import io.orprotocol.client.Client; import io.orprotocol.packet.PacketExecutor; +import io.orprotocol.packet.PacketManager; public class Executor20GroupJoin extends PacketExecutor { private final GroupRepository groupRepository = new GroupRepository(); + private final BufferRepository bufferRepository = new BufferRepository(); + private final BufferService bufferService; + + public Executor20GroupJoin(PacketManager packetManager) { + this.bufferService = new BufferService(bufferRepository, packetManager); + } @Override public void onPacketReceived(Packet20GroupJoin packet, Client client) throws Exception, ProtocolException { @@ -53,12 +61,21 @@ public class Executor20GroupJoin extends PacketExecutor { client.send(packet); return; } - + /** + * Все проверки пройдены, клиент может вступить в группу + */ + packet.setStatus(NetworkGroupStatus.JOINED); + /** + * Кладем пакет в буфер для будущей синхронизации + */ + this.bufferService.pushPacketToBuffer("server", eciAuthentificate.getPublicKey(), packet); /** * Добавляем клиента в группу и возвращаем клиенту статус JOINED */ this.groupRepository.addMemberToGroup(groupId, eciAuthentificate.getPublicKey()); - packet.setStatus(NetworkGroupStatus.JOINED); + /** + * Возвращаем клиенту ответный пакет с статусом JOINED + */ client.send(packet); } diff --git a/src/main/java/im/rosetta/packet/Packet20GroupJoin.java b/src/main/java/im/rosetta/packet/Packet20GroupJoin.java index 1abbcc1..599070d 100644 --- a/src/main/java/im/rosetta/packet/Packet20GroupJoin.java +++ b/src/main/java/im/rosetta/packet/Packet20GroupJoin.java @@ -14,11 +14,20 @@ public class Packet20GroupJoin extends Packet { private String groupId; private NetworkGroupStatus status; + /** + * Строка группы, которая содержит информацию о группе, такую как ее название, описание и ключ + * Строка зашифрована обратимым шифрованием, где ключом выступает - реальный приватный ключ + * входящего в группу клиента. Нужно это для будущей синхронзации, так как клиенту на его другом + * устройстве нужно получить ключ группы и ее информацию. Сервер расшифровать эту строку не может. Эту + * строку может расшифровать только клиент, так как она зашифрована его приватным ключом + */ + private String groupString; @Override public void read(Stream stream) { this.groupId = stream.readString(); this.status = NetworkGroupStatus.fromCode(stream.readInt8()); + this.groupString = stream.readString(); } @Override @@ -27,6 +36,7 @@ public class Packet20GroupJoin extends Packet { stream.writeInt16(this.packetId); stream.writeString(this.groupId); stream.writeInt8(this.status.getCode()); + stream.writeString(this.groupString); return stream; } @@ -62,4 +72,20 @@ public class Packet20GroupJoin extends Packet { this.status = status; } + /** + * Получить строку группы, которая содержит информацию о группе, такую как ее название, описание и ключ + * @return строка группы + */ + public String getGroupString() { + return groupString; + } + + /** + * Установить строку группы, которая содержит информацию о группе, такую как ее название, описание и ключ + * @param groupString строка группы + */ + public void setGroupString(String groupString) { + this.groupString = groupString; + } + } diff --git a/src/main/java/im/rosetta/service/dispatch/MessageDispatcher.java b/src/main/java/im/rosetta/service/dispatch/MessageDispatcher.java index 0644ebd..2141f9e 100644 --- a/src/main/java/im/rosetta/service/dispatch/MessageDispatcher.java +++ b/src/main/java/im/rosetta/service/dispatch/MessageDispatcher.java @@ -1,6 +1,5 @@ package im.rosetta.service.dispatch; -import java.util.HashSet; import java.util.List; import im.rosetta.client.ClientManager; @@ -57,7 +56,21 @@ public class MessageDispatcher { return; } /** - * Отправляем всем участникам группы, кроме отправителя этот пакет, попутно не забывая проверить, а не один ли он в группе + * Ретранслируем сообщение ВСЕМ авторизованным сессиям отправителя КРОМЕ текущей, + * чтобы синхронизировать отправленные сообщения + */ + if(!(packet instanceof Packet11Typeing)){ + /** + * Если это пакет печати его не обязательно кэшировать, так как он нужен только + * для отображения статуса печати в реальном времени + * Кладем пакет в буфер для будущей синхронизации и на случай если кто-то из участников оффлайн, + * в toPublicKey при отправке в группу у нас находится #group:groupId + */ + this.bufferService.pushPacketToBuffer(fromPublicKey, toPublicKey.replace("#group:", ""), packet); + this.clientManager.retranslate(client, packet); + } + /** + * Отправляем всем участникам группы, кроме отправителя, попутно проверяем, а не один ли он в группе */ groupMembersPublicKeys.remove(eciAuthentificate.getPublicKey()); if(groupMembersPublicKeys.isEmpty()){ @@ -67,20 +80,10 @@ public class MessageDispatcher { */ return; } - this.clientManager.sendPacketToAuthorizedPK(groupMembersPublicKeys, packet); - - if(packet instanceof Packet11Typeing){ - /** - * Если это пакет печати его не обязательно кэшировать, так как он нужен только - * для отображения статуса печати в реальном времени - */ - return; - } /** - * Кладем пакет в буфер для будущей синхронизации и на случай если кто-то из участников оффлайн, - * в toPublicKey при отправке в группу у нас находится #group:groupId + * Отправляем сообщение всем, кто в беседе */ - this.bufferService.pushPacketToBuffer(fromPublicKey, toPublicKey.replace("#group:", ""), packet); + this.clientManager.sendPacketToAuthorizedPK(groupMembersPublicKeys, packet); } /** @@ -94,6 +97,12 @@ public class MessageDispatcher { public void sendPeer(PacketBaseDialog packet, Client client, boolean bufferizationNeed) throws ProtocolException { String fromPublicKey = packet.getFromPublicKey(); String toPublicKey = packet.getToPublicKey(); + + /** + * Ретранслируем сообщение ВСЕМ авторизованным сессиям отправителя КРОМЕ текущей, + * чтобы синхронизировать отправленные сообщения + */ + this.clientManager.retranslate(client, packet); this.clientManager.sendPacketToAuthorizedPK(toPublicKey, packet); if(!bufferizationNeed){ @@ -107,11 +116,6 @@ public class MessageDispatcher { * Сохраняем сообщение в буфер на случай если получатель офлайн, или нам нужна будет синхронизация сообщений для получателя */ this.bufferService.pushPacketToBuffer(fromPublicKey, toPublicKey, packet); - - /** - * Ретранслируем сообщение всем авторизованным сессиям отправителя, чтобы синхронизировать отправленные сообщения - */ - this.retranslate(packet, client); } /** @@ -127,34 +131,4 @@ public class MessageDispatcher { this.sendPeer(packet, client, true); } - /** - * Сообщает всем авторизованным сессиям отправителя о том, что он отправил сообщения, - * для того чтобы синхронизировать отправленные сообщения на всех устройствах отправителя - * @param packet пакет сообщения - * @param client клиент отправляющий пакет - * @throws ProtocolException - */ - public void retranslate(PacketBaseDialog packet, Client client) throws ProtocolException { - ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class); - HashSet clients = this.clientManager.getClientIndexer() - .getClients(ECIAuthentificate.class, "publicKey", eciAuthentificate.getPublicKey()); - if(clients == null){ - /** - * Нет авторизованных сессий с таким публичным ключом - */ - return; - } - for(Client c : clients){ - /** - * Проходим по всем устройствам с таким публичным ключом и ретранслируем им пакет, кроме того устройства что - * отправило пакет - */ - if(c.equals(client)){ - continue; - } - c.send(packet); - } - } - - } -- 2.49.1