mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
Refactor to use GroupId class to wrap the byte array
Helps distinguish between group v1 and v2 ids
This commit is contained in:
parent
67f62947c6
commit
9942d967a4
31 changed files with 358 additions and 228 deletions
|
@ -65,7 +65,7 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler {
|
||||||
} else if (content.getDataMessage().isPresent()) {
|
} else if (content.getDataMessage().isPresent()) {
|
||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
|
|
||||||
byte[] groupId = getGroupId(m, message);
|
byte[] groupId = getGroupId(message);
|
||||||
if (!message.isEndSession() && (
|
if (!message.isEndSession() && (
|
||||||
groupId == null
|
groupId == null
|
||||||
|| message.getGroupContext().get().getGroupV1Type() == null
|
|| message.getGroupContext().get().getGroupV1Type() == null
|
||||||
|
@ -91,7 +91,7 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler {
|
||||||
.getGroupContext()
|
.getGroupContext()
|
||||||
.isPresent()) {
|
.isPresent()) {
|
||||||
SignalServiceDataMessage message = transcript.getMessage();
|
SignalServiceDataMessage message = transcript.getMessage();
|
||||||
byte[] groupId = getGroupId(m, message);
|
byte[] groupId = getGroupId(message);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
conn.sendMessage(new Signal.SyncMessageReceived(objectPath,
|
conn.sendMessage(new Signal.SyncMessageReceived(objectPath,
|
||||||
|
@ -112,20 +112,9 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] getGroupId(final Manager m, final SignalServiceDataMessage message) {
|
private static byte[] getGroupId(final SignalServiceDataMessage message) {
|
||||||
byte[] groupId;
|
return message.getGroupContext().isPresent() ? GroupUtils.getGroupId(message.getGroupContext().get())
|
||||||
if (message.getGroupContext().isPresent()) {
|
.serialize() : null;
|
||||||
if (message.getGroupContext().get().getGroupV1().isPresent()) {
|
|
||||||
groupId = message.getGroupContext().get().getGroupV1().get().getGroupId();
|
|
||||||
} else if (message.getGroupContext().get().getGroupV2().isPresent()) {
|
|
||||||
groupId = GroupUtils.getGroupId(message.getGroupContext().get().getGroupV2().get().getMasterKey());
|
|
||||||
} else {
|
|
||||||
groupId = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
groupId = null;
|
|
||||||
}
|
|
||||||
return groupId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static private List<String> getAttachments(SignalServiceDataMessage message, Manager m) {
|
static private List<String> getAttachments(SignalServiceDataMessage message, Manager m) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.asamk.signal;
|
package org.asamk.signal;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
import org.asamk.signal.manager.GroupUtils;
|
import org.asamk.signal.manager.GroupUtils;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.storage.contacts.ContactInfo;
|
import org.asamk.signal.storage.contacts.ContactInfo;
|
||||||
|
@ -328,8 +329,9 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
||||||
System.out.println(" - Timestamp: " + DateUtils.formatTimestamp(typingMessage.getTimestamp()));
|
System.out.println(" - Timestamp: " + DateUtils.formatTimestamp(typingMessage.getTimestamp()));
|
||||||
if (typingMessage.getGroupId().isPresent()) {
|
if (typingMessage.getGroupId().isPresent()) {
|
||||||
System.out.println(" - Group Info:");
|
System.out.println(" - Group Info:");
|
||||||
System.out.println(" Id: " + Base64.encodeBytes(typingMessage.getGroupId().get()));
|
final GroupId groupId = GroupId.unknownVersion(typingMessage.getGroupId().get());
|
||||||
GroupInfo group = m.getGroup(typingMessage.getGroupId().get());
|
System.out.println(" Id: " + groupId.toBase64());
|
||||||
|
GroupInfo group = m.getGroup(groupId);
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
System.out.println(" Name: " + group.getTitle());
|
System.out.println(" Name: " + group.getTitle());
|
||||||
} else {
|
} else {
|
||||||
|
@ -356,13 +358,14 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
||||||
if (message.getGroupContext().isPresent()) {
|
if (message.getGroupContext().isPresent()) {
|
||||||
System.out.println("Group info:");
|
System.out.println("Group info:");
|
||||||
final SignalServiceGroupContext groupContext = message.getGroupContext().get();
|
final SignalServiceGroupContext groupContext = message.getGroupContext().get();
|
||||||
|
final GroupId groupId = GroupUtils.getGroupId(groupContext);
|
||||||
if (groupContext.getGroupV1().isPresent()) {
|
if (groupContext.getGroupV1().isPresent()) {
|
||||||
SignalServiceGroup groupInfo = groupContext.getGroupV1().get();
|
SignalServiceGroup groupInfo = groupContext.getGroupV1().get();
|
||||||
System.out.println(" Id: " + Base64.encodeBytes(groupInfo.getGroupId()));
|
System.out.println(" Id: " + groupId.toBase64());
|
||||||
if (groupInfo.getType() == SignalServiceGroup.Type.UPDATE && groupInfo.getName().isPresent()) {
|
if (groupInfo.getType() == SignalServiceGroup.Type.UPDATE && groupInfo.getName().isPresent()) {
|
||||||
System.out.println(" Name: " + groupInfo.getName().get());
|
System.out.println(" Name: " + groupInfo.getName().get());
|
||||||
} else {
|
} else {
|
||||||
GroupInfo group = m.getGroup(groupInfo.getGroupId());
|
GroupInfo group = m.getGroup(groupId);
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
System.out.println(" Name: " + group.getTitle());
|
System.out.println(" Name: " + group.getTitle());
|
||||||
} else {
|
} else {
|
||||||
|
@ -381,8 +384,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
||||||
}
|
}
|
||||||
} else if (groupContext.getGroupV2().isPresent()) {
|
} else if (groupContext.getGroupV2().isPresent()) {
|
||||||
final SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get();
|
final SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get();
|
||||||
byte[] groupId = GroupUtils.getGroupId(groupInfo.getMasterKey());
|
System.out.println(" Id: " + groupId.toBase64());
|
||||||
System.out.println(" Id: " + Base64.encodeBytes(groupId));
|
|
||||||
GroupInfo group = m.getGroup(groupId);
|
GroupInfo group = m.getGroup(groupId);
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
System.out.println(" Name: " + group.getTitle());
|
System.out.println(" Name: " + group.getTitle());
|
||||||
|
|
|
@ -3,9 +3,10 @@ package org.asamk.signal.commands;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
|
import org.asamk.signal.manager.GroupIdFormatException;
|
||||||
import org.asamk.signal.manager.GroupNotFoundException;
|
import org.asamk.signal.manager.GroupNotFoundException;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.util.GroupIdFormatException;
|
|
||||||
import org.asamk.signal.util.Util;
|
import org.asamk.signal.util.Util;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ public class BlockCommand implements LocalCommand {
|
||||||
if (ns.<String>getList("group") != null) {
|
if (ns.<String>getList("group") != null) {
|
||||||
for (String groupIdString : ns.<String>getList("group")) {
|
for (String groupIdString : ns.<String>getList("group")) {
|
||||||
try {
|
try {
|
||||||
byte[] groupId = Util.decodeGroupId(groupIdString);
|
GroupId groupId = Util.decodeGroupId(groupIdString);
|
||||||
m.setGroupBlocked(groupId, true);
|
m.setGroupBlocked(groupId, true);
|
||||||
} catch (GroupIdFormatException | GroupNotFoundException e) {
|
} catch (GroupIdFormatException | GroupNotFoundException e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
|
|
|
@ -4,6 +4,7 @@ import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.Signal;
|
import org.asamk.Signal;
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
import org.asamk.signal.manager.GroupInviteLinkUrl;
|
import org.asamk.signal.manager.GroupInviteLinkUrl;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
||||||
|
@ -11,7 +12,6 @@ import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
||||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||||
import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException;
|
import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException;
|
||||||
import org.whispersystems.util.Base64;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -52,12 +52,12 @@ public class JoinGroupCommand implements LocalCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Pair<byte[], List<SendMessageResult>> results = m.joinGroup(linkUrl);
|
final Pair<GroupId, List<SendMessageResult>> results = m.joinGroup(linkUrl);
|
||||||
byte[] newGroupId = results.first();
|
GroupId newGroupId = results.first();
|
||||||
if (!m.getGroup(newGroupId).isMember(m.getSelfAddress())) {
|
if (!m.getGroup(newGroupId).isMember(m.getSelfAddress())) {
|
||||||
System.out.println("Requested to join group \"" + Base64.encodeBytes(newGroupId) + "\"");
|
System.out.println("Requested to join group \"" + newGroupId.toBase64() + "\"");
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Joined group \"" + Base64.encodeBytes(newGroupId) + "\"");
|
System.out.println("Joined group \"" + newGroupId.toBase64() + "\"");
|
||||||
}
|
}
|
||||||
return handleTimestampAndSendMessageResults(0, results.second());
|
return handleTimestampAndSendMessageResults(0, results.second());
|
||||||
} catch (AssertionError e) {
|
} catch (AssertionError e) {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import org.asamk.signal.manager.GroupInviteLinkUrl;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.storage.groups.GroupInfo;
|
import org.asamk.signal.storage.groups.GroupInfo;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.util.Base64;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -40,7 +39,7 @@ public class ListGroupsCommand implements LocalCommand {
|
||||||
|
|
||||||
System.out.println(String.format(
|
System.out.println(String.format(
|
||||||
"Id: %s Name: %s Active: %s Blocked: %b Members: %s Pending members: %s Requesting members: %s Link: %s",
|
"Id: %s Name: %s Active: %s Blocked: %b Members: %s Pending members: %s Requesting members: %s Link: %s",
|
||||||
Base64.encodeBytes(group.groupId),
|
group.getGroupId().toBase64(),
|
||||||
group.getTitle(),
|
group.getTitle(),
|
||||||
group.isMember(m.getSelfAddress()),
|
group.isMember(m.getSelfAddress()),
|
||||||
group.isBlocked(),
|
group.isBlocked(),
|
||||||
|
@ -50,7 +49,7 @@ public class ListGroupsCommand implements LocalCommand {
|
||||||
groupInviteLink == null ? '-' : groupInviteLink.getUrl()));
|
groupInviteLink == null ? '-' : groupInviteLink.getUrl()));
|
||||||
} else {
|
} else {
|
||||||
System.out.println(String.format("Id: %s Name: %s Active: %s Blocked: %b",
|
System.out.println(String.format("Id: %s Name: %s Active: %s Blocked: %b",
|
||||||
Base64.encodeBytes(group.groupId),
|
group.getGroupId().toBase64(),
|
||||||
group.getTitle(),
|
group.getTitle(),
|
||||||
group.isMember(m.getSelfAddress()),
|
group.isMember(m.getSelfAddress()),
|
||||||
group.isBlocked()));
|
group.isBlocked()));
|
||||||
|
|
|
@ -3,10 +3,11 @@ package org.asamk.signal.commands;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
|
import org.asamk.signal.manager.GroupIdFormatException;
|
||||||
import org.asamk.signal.manager.GroupNotFoundException;
|
import org.asamk.signal.manager.GroupNotFoundException;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.manager.NotAGroupMemberException;
|
import org.asamk.signal.manager.NotAGroupMemberException;
|
||||||
import org.asamk.signal.util.GroupIdFormatException;
|
|
||||||
import org.asamk.signal.util.Util;
|
import org.asamk.signal.util.Util;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||||
|
@ -36,7 +37,7 @@ public class QuitGroupCommand implements LocalCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final byte[] groupId = Util.decodeGroupId(ns.getString("group"));
|
final GroupId groupId = Util.decodeGroupId(ns.getString("group"));
|
||||||
final Pair<Long, List<SendMessageResult>> results = m.sendQuitGroupMessage(groupId);
|
final Pair<Long, List<SendMessageResult>> results = m.sendQuitGroupMessage(groupId);
|
||||||
return handleTimestampAndSendMessageResults(results.first(), results.second());
|
return handleTimestampAndSendMessageResults(results.first(), results.second());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.Signal;
|
import org.asamk.Signal;
|
||||||
import org.asamk.signal.util.GroupIdFormatException;
|
import org.asamk.signal.manager.GroupIdFormatException;
|
||||||
import org.asamk.signal.util.IOUtils;
|
import org.asamk.signal.util.IOUtils;
|
||||||
import org.asamk.signal.util.Util;
|
import org.asamk.signal.util.Util;
|
||||||
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
||||||
|
@ -79,7 +79,7 @@ public class SendCommand implements DbusCommand {
|
||||||
if (ns.getString("group") != null) {
|
if (ns.getString("group") != null) {
|
||||||
byte[] groupId;
|
byte[] groupId;
|
||||||
try {
|
try {
|
||||||
groupId = Util.decodeGroupId(ns.getString("group"));
|
groupId = Util.decodeGroupId(ns.getString("group")).serialize();
|
||||||
} catch (GroupIdFormatException e) {
|
} catch (GroupIdFormatException e) {
|
||||||
handleGroupIdFormatException(e);
|
handleGroupIdFormatException(e);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -4,10 +4,11 @@ import net.sourceforge.argparse4j.impl.Arguments;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
|
import org.asamk.signal.manager.GroupIdFormatException;
|
||||||
import org.asamk.signal.manager.GroupNotFoundException;
|
import org.asamk.signal.manager.GroupNotFoundException;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.manager.NotAGroupMemberException;
|
import org.asamk.signal.manager.NotAGroupMemberException;
|
||||||
import org.asamk.signal.util.GroupIdFormatException;
|
|
||||||
import org.asamk.signal.util.Util;
|
import org.asamk.signal.util.Util;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||||
|
@ -65,7 +66,7 @@ public class SendReactionCommand implements LocalCommand {
|
||||||
try {
|
try {
|
||||||
final Pair<Long, List<SendMessageResult>> results;
|
final Pair<Long, List<SendMessageResult>> results;
|
||||||
if (ns.getString("group") != null) {
|
if (ns.getString("group") != null) {
|
||||||
byte[] groupId = Util.decodeGroupId(ns.getString("group"));
|
GroupId groupId = Util.decodeGroupId(ns.getString("group"));
|
||||||
results = m.sendGroupMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, groupId);
|
results = m.sendGroupMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, groupId);
|
||||||
} else {
|
} else {
|
||||||
results = m.sendMessageReaction(emoji,
|
results = m.sendMessageReaction(emoji,
|
||||||
|
|
|
@ -3,9 +3,10 @@ package org.asamk.signal.commands;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
|
import org.asamk.signal.manager.GroupIdFormatException;
|
||||||
import org.asamk.signal.manager.GroupNotFoundException;
|
import org.asamk.signal.manager.GroupNotFoundException;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.util.GroupIdFormatException;
|
|
||||||
import org.asamk.signal.util.Util;
|
import org.asamk.signal.util.Util;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ public class UnblockCommand implements LocalCommand {
|
||||||
if (ns.<String>getList("group") != null) {
|
if (ns.<String>getList("group") != null) {
|
||||||
for (String groupIdString : ns.<String>getList("group")) {
|
for (String groupIdString : ns.<String>getList("group")) {
|
||||||
try {
|
try {
|
||||||
byte[] groupId = Util.decodeGroupId(groupIdString);
|
GroupId groupId = Util.decodeGroupId(groupIdString);
|
||||||
m.setGroupBlocked(groupId, false);
|
m.setGroupBlocked(groupId, false);
|
||||||
} catch (GroupIdFormatException | GroupNotFoundException e) {
|
} catch (GroupIdFormatException | GroupNotFoundException e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
|
|
|
@ -4,7 +4,7 @@ import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.Signal;
|
import org.asamk.Signal;
|
||||||
import org.asamk.signal.util.GroupIdFormatException;
|
import org.asamk.signal.manager.GroupIdFormatException;
|
||||||
import org.asamk.signal.util.Util;
|
import org.asamk.signal.util.Util;
|
||||||
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
||||||
import org.whispersystems.util.Base64;
|
import org.whispersystems.util.Base64;
|
||||||
|
@ -35,7 +35,7 @@ public class UpdateGroupCommand implements DbusCommand {
|
||||||
byte[] groupId = null;
|
byte[] groupId = null;
|
||||||
if (ns.getString("group") != null) {
|
if (ns.getString("group") != null) {
|
||||||
try {
|
try {
|
||||||
groupId = Util.decodeGroupId(ns.getString("group"));
|
groupId = Util.decodeGroupId(ns.getString("group")).serialize();
|
||||||
} catch (GroupIdFormatException e) {
|
} catch (GroupIdFormatException e) {
|
||||||
handleGroupIdFormatException(e);
|
handleGroupIdFormatException(e);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.asamk.signal.dbus;
|
||||||
|
|
||||||
import org.asamk.Signal;
|
import org.asamk.Signal;
|
||||||
import org.asamk.signal.manager.AttachmentInvalidException;
|
import org.asamk.signal.manager.AttachmentInvalidException;
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
import org.asamk.signal.manager.GroupNotFoundException;
|
import org.asamk.signal.manager.GroupNotFoundException;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.manager.NotAGroupMemberException;
|
import org.asamk.signal.manager.NotAGroupMemberException;
|
||||||
|
@ -92,7 +93,9 @@ public class DbusSignalImpl implements Signal {
|
||||||
@Override
|
@Override
|
||||||
public long sendGroupMessage(final String message, final List<String> attachments, final byte[] groupId) {
|
public long sendGroupMessage(final String message, final List<String> attachments, final byte[] groupId) {
|
||||||
try {
|
try {
|
||||||
Pair<Long, List<SendMessageResult>> results = m.sendGroupMessage(message, attachments, groupId);
|
Pair<Long, List<SendMessageResult>> results = m.sendGroupMessage(message,
|
||||||
|
attachments,
|
||||||
|
GroupId.unknownVersion(groupId));
|
||||||
checkSendMessageResults(results.first(), results.second());
|
checkSendMessageResults(results.first(), results.second());
|
||||||
return results.first();
|
return results.first();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -134,7 +137,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
@Override
|
@Override
|
||||||
public void setGroupBlocked(final byte[] groupId, final boolean blocked) {
|
public void setGroupBlocked(final byte[] groupId, final boolean blocked) {
|
||||||
try {
|
try {
|
||||||
m.setGroupBlocked(groupId, blocked);
|
m.setGroupBlocked(GroupId.unknownVersion(groupId), blocked);
|
||||||
} catch (GroupNotFoundException e) {
|
} catch (GroupNotFoundException e) {
|
||||||
throw new Error.GroupNotFound(e.getMessage());
|
throw new Error.GroupNotFound(e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -145,14 +148,14 @@ public class DbusSignalImpl implements Signal {
|
||||||
List<GroupInfo> groups = m.getGroups();
|
List<GroupInfo> groups = m.getGroups();
|
||||||
List<byte[]> ids = new ArrayList<>(groups.size());
|
List<byte[]> ids = new ArrayList<>(groups.size());
|
||||||
for (GroupInfo group : groups) {
|
for (GroupInfo group : groups) {
|
||||||
ids.add(group.groupId);
|
ids.add(group.getGroupId().serialize());
|
||||||
}
|
}
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getGroupName(final byte[] groupId) {
|
public String getGroupName(final byte[] groupId) {
|
||||||
GroupInfo group = m.getGroup(groupId);
|
GroupInfo group = m.getGroup(GroupId.unknownVersion(groupId));
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
|
@ -162,7 +165,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getGroupMembers(final byte[] groupId) {
|
public List<String> getGroupMembers(final byte[] groupId) {
|
||||||
GroupInfo group = m.getGroup(groupId);
|
GroupInfo group = m.getGroup(GroupId.unknownVersion(groupId));
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
} else {
|
} else {
|
||||||
|
@ -189,9 +192,11 @@ public class DbusSignalImpl implements Signal {
|
||||||
if (avatar.isEmpty()) {
|
if (avatar.isEmpty()) {
|
||||||
avatar = null;
|
avatar = null;
|
||||||
}
|
}
|
||||||
final Pair<byte[], List<SendMessageResult>> results = m.updateGroup(groupId, name, members, avatar);
|
final Pair<GroupId, List<SendMessageResult>> results = m.updateGroup(groupId == null
|
||||||
|
? null
|
||||||
|
: GroupId.unknownVersion(groupId), name, members, avatar);
|
||||||
checkSendMessageResults(0, results.second());
|
checkSendMessageResults(0, results.second());
|
||||||
return results.first();
|
return results.first().serialize();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new Error.Failure(e.getMessage());
|
throw new Error.Failure(e.getMessage());
|
||||||
} catch (GroupNotFoundException | NotAGroupMemberException e) {
|
} catch (GroupNotFoundException | NotAGroupMemberException e) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ class JsonGroupInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonGroupInfo(SignalServiceGroupV2 groupInfo) {
|
JsonGroupInfo(SignalServiceGroupV2 groupInfo) {
|
||||||
this.groupId = Base64.encodeBytes(GroupUtils.getGroupId(groupInfo.getMasterKey()));
|
this.groupId = GroupUtils.getGroupIdV2(groupInfo.getMasterKey()).toBase64();
|
||||||
this.type = groupInfo.hasSignedGroupChange() ? "UPDATE" : "DELIVER";
|
this.type = groupInfo.hasSignedGroupChange() ? "UPDATE" : "DELIVER";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
63
src/main/java/org/asamk/signal/manager/GroupId.java
Normal file
63
src/main/java/org/asamk/signal/manager/GroupId.java
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
|
import org.whispersystems.util.Base64;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public abstract class GroupId {
|
||||||
|
|
||||||
|
private final byte[] id;
|
||||||
|
|
||||||
|
public static GroupIdV1 v1(byte[] id) {
|
||||||
|
return new GroupIdV1(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupIdV2 v2(byte[] id) {
|
||||||
|
return new GroupIdV2(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupId unknownVersion(byte[] id) {
|
||||||
|
if (id.length == 16) {
|
||||||
|
return new GroupIdV1(id);
|
||||||
|
} else if (id.length == 32) {
|
||||||
|
return new GroupIdV2(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AssertionError("Invalid group id of size " + id.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupId fromBase64(String id) throws GroupIdFormatException {
|
||||||
|
try {
|
||||||
|
return unknownVersion(java.util.Base64.getDecoder().decode(id));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new GroupIdFormatException(id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupId(final byte[] id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] serialize() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toBase64() {
|
||||||
|
return Base64.encodeBytes(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
final GroupId groupId = (GroupId) o;
|
||||||
|
|
||||||
|
return Arrays.equals(id, groupId.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
|
public class GroupIdFormatException extends Exception {
|
||||||
|
|
||||||
|
public GroupIdFormatException(String groupId, Throwable e) {
|
||||||
|
super("Failed to decode groupId (must be base64) \"" + groupId + "\": " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/org/asamk/signal/manager/GroupIdV1.java
Normal file
14
src/main/java/org/asamk/signal/manager/GroupIdV1.java
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
|
import static org.asamk.signal.manager.KeyUtils.getSecretBytes;
|
||||||
|
|
||||||
|
public class GroupIdV1 extends GroupId {
|
||||||
|
|
||||||
|
public static GroupIdV1 createRandom() {
|
||||||
|
return new GroupIdV1(getSecretBytes(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupIdV1(final byte[] id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/org/asamk/signal/manager/GroupIdV2.java
Normal file
14
src/main/java/org/asamk/signal/manager/GroupIdV2.java
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class GroupIdV2 extends GroupId {
|
||||||
|
|
||||||
|
public static GroupIdV2 fromBase64(String groupId) {
|
||||||
|
return new GroupIdV2(Base64.getDecoder().decode(groupId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupIdV2(final byte[] id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,8 @@
|
||||||
package org.asamk.signal.manager;
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
import org.whispersystems.util.Base64;
|
|
||||||
|
|
||||||
public class GroupNotFoundException extends Exception {
|
public class GroupNotFoundException extends Exception {
|
||||||
|
|
||||||
public GroupNotFoundException(byte[] groupId) {
|
public GroupNotFoundException(GroupId groupId) {
|
||||||
super("Group not found: " + Base64.encodeBytes(groupId));
|
super("Group not found: " + groupId.toBase64());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.signal.zkgroup.groups.GroupSecretParams;
|
||||||
import org.whispersystems.libsignal.kdf.HKDFv3;
|
import org.whispersystems.libsignal.kdf.HKDFv3;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||||
|
|
||||||
public class GroupUtils {
|
public class GroupUtils {
|
||||||
|
@ -18,7 +19,7 @@ public class GroupUtils {
|
||||||
) {
|
) {
|
||||||
if (groupInfo instanceof GroupInfoV1) {
|
if (groupInfo instanceof GroupInfoV1) {
|
||||||
SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.DELIVER)
|
SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.DELIVER)
|
||||||
.withId(groupInfo.groupId)
|
.withId(groupInfo.getGroupId().serialize())
|
||||||
.build();
|
.build();
|
||||||
messageBuilder.asGroupMessage(group);
|
messageBuilder.asGroupMessage(group);
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,14 +31,34 @@ public class GroupUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getGroupId(GroupMasterKey groupMasterKey) {
|
public static GroupId getGroupId(SignalServiceGroupContext context) {
|
||||||
final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
if (context.getGroupV1().isPresent()) {
|
||||||
return groupSecretParams.getPublicParams().getGroupIdentifier().serialize();
|
return GroupId.v1(context.getGroupV1().get().getGroupId());
|
||||||
|
} else if (context.getGroupV2().isPresent()) {
|
||||||
|
return getGroupIdV2(context.getGroupV2().get().getMasterKey());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GroupMasterKey deriveV2MigrationMasterKey(byte[] groupIdV1) {
|
public static GroupIdV2 getGroupIdV2(GroupSecretParams groupSecretParams) {
|
||||||
|
return GroupId.v2(groupSecretParams.getPublicParams().getGroupIdentifier().serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupIdV2 getGroupIdV2(GroupMasterKey groupMasterKey) {
|
||||||
|
final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
||||||
|
return getGroupIdV2(groupSecretParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupIdV2 getGroupIdV2(GroupIdV1 groupIdV1) {
|
||||||
|
final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(deriveV2MigrationMasterKey(
|
||||||
|
groupIdV1));
|
||||||
|
return getGroupIdV2(groupSecretParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GroupMasterKey deriveV2MigrationMasterKey(GroupIdV1 groupIdV1) {
|
||||||
try {
|
try {
|
||||||
return new GroupMasterKey(new HKDFv3().deriveSecrets(groupIdV1,
|
return new GroupMasterKey(new HKDFv3().deriveSecrets(groupIdV1.serialize(),
|
||||||
"GV2 Migration".getBytes(),
|
"GV2 Migration".getBytes(),
|
||||||
GroupMasterKey.SIZE));
|
GroupMasterKey.SIZE));
|
||||||
} catch (InvalidInputException e) {
|
} catch (InvalidInputException e) {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.asamk.signal.manager;
|
||||||
|
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
interface HandleAction {
|
interface HandleAction {
|
||||||
|
@ -93,9 +92,9 @@ class SendSyncBlockedListAction implements HandleAction {
|
||||||
class SendGroupInfoRequestAction implements HandleAction {
|
class SendGroupInfoRequestAction implements HandleAction {
|
||||||
|
|
||||||
private final SignalServiceAddress address;
|
private final SignalServiceAddress address;
|
||||||
private final byte[] groupId;
|
private final GroupIdV1 groupId;
|
||||||
|
|
||||||
public SendGroupInfoRequestAction(final SignalServiceAddress address, final byte[] groupId) {
|
public SendGroupInfoRequestAction(final SignalServiceAddress address, final GroupIdV1 groupId) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
@ -109,14 +108,17 @@ class SendGroupInfoRequestAction implements HandleAction {
|
||||||
public boolean equals(final Object o) {
|
public boolean equals(final Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
final SendGroupInfoRequestAction that = (SendGroupInfoRequestAction) o;
|
final SendGroupInfoRequestAction that = (SendGroupInfoRequestAction) o;
|
||||||
return address.equals(that.address) && Arrays.equals(groupId, that.groupId);
|
|
||||||
|
if (!address.equals(that.address)) return false;
|
||||||
|
return groupId.equals(that.groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = Objects.hash(address);
|
int result = address.hashCode();
|
||||||
result = 31 * result + Arrays.hashCode(groupId);
|
result = 31 * result + groupId.hashCode();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,9 +126,9 @@ class SendGroupInfoRequestAction implements HandleAction {
|
||||||
class SendGroupUpdateAction implements HandleAction {
|
class SendGroupUpdateAction implements HandleAction {
|
||||||
|
|
||||||
private final SignalServiceAddress address;
|
private final SignalServiceAddress address;
|
||||||
private final byte[] groupId;
|
private final GroupIdV1 groupId;
|
||||||
|
|
||||||
public SendGroupUpdateAction(final SignalServiceAddress address, final byte[] groupId) {
|
public SendGroupUpdateAction(final SignalServiceAddress address, final GroupIdV1 groupId) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
@ -140,14 +142,17 @@ class SendGroupUpdateAction implements HandleAction {
|
||||||
public boolean equals(final Object o) {
|
public boolean equals(final Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
final SendGroupUpdateAction that = (SendGroupUpdateAction) o;
|
final SendGroupUpdateAction that = (SendGroupUpdateAction) o;
|
||||||
return address.equals(that.address) && Arrays.equals(groupId, that.groupId);
|
|
||||||
|
if (!address.equals(that.address)) return false;
|
||||||
|
return groupId.equals(that.groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = Objects.hash(address);
|
int result = address.hashCode();
|
||||||
result = 31 * result + Arrays.hashCode(groupId);
|
result = 31 * result + groupId.hashCode();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,6 @@ class KeyUtils {
|
||||||
return getSecret(18);
|
return getSecret(18);
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] createGroupId() {
|
|
||||||
return getSecretBytes(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
static byte[] createStickerUploadKey() {
|
static byte[] createStickerUploadKey() {
|
||||||
return getSecretBytes(32);
|
return getSecretBytes(32);
|
||||||
}
|
}
|
||||||
|
|
|
@ -679,7 +679,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<SignalServiceAttachmentStream> createGroupAvatarAttachment(byte[] groupId) throws IOException {
|
private Optional<SignalServiceAttachmentStream> createGroupAvatarAttachment(GroupId groupId) throws IOException {
|
||||||
File file = getGroupAvatarFile(groupId);
|
File file = getGroupAvatarFile(groupId);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
return Optional.absent();
|
return Optional.absent();
|
||||||
|
@ -697,7 +697,7 @@ public class Manager implements Closeable {
|
||||||
return Optional.of(Utils.createAttachment(file));
|
return Optional.of(Utils.createAttachment(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupInfo getGroupForSending(byte[] groupId) throws GroupNotFoundException, NotAGroupMemberException {
|
private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException {
|
||||||
GroupInfo g = account.getGroupStore().getGroup(groupId);
|
GroupInfo g = account.getGroupStore().getGroup(groupId);
|
||||||
if (g == null) {
|
if (g == null) {
|
||||||
throw new GroupNotFoundException(groupId);
|
throw new GroupNotFoundException(groupId);
|
||||||
|
@ -708,7 +708,7 @@ public class Manager implements Closeable {
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupInfo getGroupForUpdating(byte[] groupId) throws GroupNotFoundException, NotAGroupMemberException {
|
private GroupInfo getGroupForUpdating(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException {
|
||||||
GroupInfo g = account.getGroupStore().getGroup(groupId);
|
GroupInfo g = account.getGroupStore().getGroup(groupId);
|
||||||
if (g == null) {
|
if (g == null) {
|
||||||
throw new GroupNotFoundException(groupId);
|
throw new GroupNotFoundException(groupId);
|
||||||
|
@ -724,7 +724,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendGroupMessage(
|
public Pair<Long, List<SendMessageResult>> sendGroupMessage(
|
||||||
SignalServiceDataMessage.Builder messageBuilder, byte[] groupId
|
SignalServiceDataMessage.Builder messageBuilder, GroupId groupId
|
||||||
) throws IOException, GroupNotFoundException, NotAGroupMemberException {
|
) throws IOException, GroupNotFoundException, NotAGroupMemberException {
|
||||||
final GroupInfo g = getGroupForSending(groupId);
|
final GroupInfo g = getGroupForSending(groupId);
|
||||||
|
|
||||||
|
@ -735,7 +735,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendGroupMessage(
|
public Pair<Long, List<SendMessageResult>> sendGroupMessage(
|
||||||
String messageText, List<String> attachments, byte[] groupId
|
String messageText, List<String> attachments, GroupId groupId
|
||||||
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
|
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
|
||||||
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
|
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
|
||||||
.withBody(messageText);
|
.withBody(messageText);
|
||||||
|
@ -747,7 +747,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendGroupMessageReaction(
|
public Pair<Long, List<SendMessageResult>> sendGroupMessageReaction(
|
||||||
String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, byte[] groupId
|
String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, GroupId groupId
|
||||||
) throws IOException, InvalidNumberException, NotAGroupMemberException, GroupNotFoundException {
|
) throws IOException, InvalidNumberException, NotAGroupMemberException, GroupNotFoundException {
|
||||||
SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji,
|
SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji,
|
||||||
remove,
|
remove,
|
||||||
|
@ -759,7 +759,7 @@ public class Manager implements Closeable {
|
||||||
return sendGroupMessage(messageBuilder, groupId);
|
return sendGroupMessage(messageBuilder, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<Long, List<SendMessageResult>> sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException {
|
public Pair<Long, List<SendMessageResult>> sendQuitGroupMessage(GroupId groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException {
|
||||||
|
|
||||||
SignalServiceDataMessage.Builder messageBuilder;
|
SignalServiceDataMessage.Builder messageBuilder;
|
||||||
|
|
||||||
|
@ -767,7 +767,7 @@ public class Manager implements Closeable {
|
||||||
if (g instanceof GroupInfoV1) {
|
if (g instanceof GroupInfoV1) {
|
||||||
GroupInfoV1 groupInfoV1 = (GroupInfoV1) g;
|
GroupInfoV1 groupInfoV1 = (GroupInfoV1) g;
|
||||||
SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT)
|
SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT)
|
||||||
.withId(groupId)
|
.withId(groupId.serialize())
|
||||||
.build();
|
.build();
|
||||||
messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group);
|
messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group);
|
||||||
groupInfoV1.removeMember(account.getSelfAddress());
|
groupInfoV1.removeMember(account.getSelfAddress());
|
||||||
|
@ -783,8 +783,8 @@ public class Manager implements Closeable {
|
||||||
return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
|
return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<byte[], List<SendMessageResult>> sendUpdateGroupMessage(
|
private Pair<GroupId, List<SendMessageResult>> sendUpdateGroupMessage(
|
||||||
byte[] groupId, String name, Collection<SignalServiceAddress> members, String avatarFile
|
GroupId groupId, String name, Collection<SignalServiceAddress> members, String avatarFile
|
||||||
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
|
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
|
||||||
GroupInfo g;
|
GroupInfo g;
|
||||||
SignalServiceDataMessage.Builder messageBuilder;
|
SignalServiceDataMessage.Builder messageBuilder;
|
||||||
|
@ -792,7 +792,7 @@ public class Manager implements Closeable {
|
||||||
// Create new group
|
// Create new group
|
||||||
GroupInfoV2 gv2 = groupHelper.createGroupV2(name, members, avatarFile);
|
GroupInfoV2 gv2 = groupHelper.createGroupV2(name, members, avatarFile);
|
||||||
if (gv2 == null) {
|
if (gv2 == null) {
|
||||||
GroupInfoV1 gv1 = new GroupInfoV1(KeyUtils.createGroupId());
|
GroupInfoV1 gv1 = new GroupInfoV1(GroupIdV1.createRandom());
|
||||||
gv1.addMembers(Collections.singleton(account.getSelfAddress()));
|
gv1.addMembers(Collections.singleton(account.getSelfAddress()));
|
||||||
updateGroupV1(gv1, name, members, avatarFile);
|
updateGroupV1(gv1, name, members, avatarFile);
|
||||||
messageBuilder = getGroupUpdateMessageBuilder(gv1);
|
messageBuilder = getGroupUpdateMessageBuilder(gv1);
|
||||||
|
@ -834,7 +834,7 @@ public class Manager implements Closeable {
|
||||||
groupGroupChangePair.second());
|
groupGroupChangePair.second());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Pair<>(group.groupId, result.second());
|
return new Pair<>(group.getGroupId(), result.second());
|
||||||
} else {
|
} else {
|
||||||
GroupInfoV1 gv1 = (GroupInfoV1) group;
|
GroupInfoV1 gv1 = (GroupInfoV1) group;
|
||||||
updateGroupV1(gv1, name, members, avatarFile);
|
updateGroupV1(gv1, name, members, avatarFile);
|
||||||
|
@ -847,16 +847,16 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
final Pair<Long, List<SendMessageResult>> result = sendMessage(messageBuilder,
|
final Pair<Long, List<SendMessageResult>> result = sendMessage(messageBuilder,
|
||||||
g.getMembersIncludingPendingWithout(account.getSelfAddress()));
|
g.getMembersIncludingPendingWithout(account.getSelfAddress()));
|
||||||
return new Pair<>(g.groupId, result.second());
|
return new Pair<>(g.getGroupId(), result.second());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<byte[], List<SendMessageResult>> joinGroup(
|
public Pair<GroupId, List<SendMessageResult>> joinGroup(
|
||||||
GroupInviteLinkUrl inviteLinkUrl
|
GroupInviteLinkUrl inviteLinkUrl
|
||||||
) throws IOException, GroupLinkNotActiveException {
|
) throws IOException, GroupLinkNotActiveException {
|
||||||
return sendJoinGroupMessage(inviteLinkUrl);
|
return sendJoinGroupMessage(inviteLinkUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<byte[], List<SendMessageResult>> sendJoinGroupMessage(
|
private Pair<GroupId, List<SendMessageResult>> sendJoinGroupMessage(
|
||||||
GroupInviteLinkUrl inviteLinkUrl
|
GroupInviteLinkUrl inviteLinkUrl
|
||||||
) throws IOException, GroupLinkNotActiveException {
|
) throws IOException, GroupLinkNotActiveException {
|
||||||
final DecryptedGroupJoinInfo groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(),
|
final DecryptedGroupJoinInfo groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(),
|
||||||
|
@ -870,12 +870,12 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
if (group.getGroup() == null) {
|
if (group.getGroup() == null) {
|
||||||
// Only requested member, can't send update to group members
|
// Only requested member, can't send update to group members
|
||||||
return new Pair<>(group.groupId, List.of());
|
return new Pair<>(group.getGroupId(), List.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
final Pair<Long, List<SendMessageResult>> result = sendUpdateGroupMessage(group, group.getGroup(), groupChange);
|
final Pair<Long, List<SendMessageResult>> result = sendUpdateGroupMessage(group, group.getGroup(), groupChange);
|
||||||
|
|
||||||
return new Pair<>(group.groupId, result.second());
|
return new Pair<>(group.getGroupId(), result.second());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Long, List<SendMessageResult>> sendUpdateGroupMessage(
|
private Pair<Long, List<SendMessageResult>> sendUpdateGroupMessage(
|
||||||
|
@ -923,13 +923,13 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
if (avatarFile != null) {
|
if (avatarFile != null) {
|
||||||
IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
|
IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
|
||||||
File aFile = getGroupAvatarFile(g.groupId);
|
File aFile = getGroupAvatarFile(g.getGroupId());
|
||||||
Files.copy(Paths.get(avatarFile), aFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(Paths.get(avatarFile), aFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<Long, List<SendMessageResult>> sendUpdateGroupMessage(
|
Pair<Long, List<SendMessageResult>> sendUpdateGroupMessage(
|
||||||
byte[] groupId, SignalServiceAddress recipient
|
GroupIdV1 groupId, SignalServiceAddress recipient
|
||||||
) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException {
|
) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException {
|
||||||
GroupInfoV1 g;
|
GroupInfoV1 g;
|
||||||
GroupInfo group = getGroupForSending(groupId);
|
GroupInfo group = getGroupForSending(groupId);
|
||||||
|
@ -950,11 +950,11 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException {
|
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException {
|
||||||
SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.UPDATE)
|
SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.UPDATE)
|
||||||
.withId(g.groupId)
|
.withId(g.getGroupId().serialize())
|
||||||
.withName(g.name)
|
.withName(g.name)
|
||||||
.withMembers(new ArrayList<>(g.getMembers()));
|
.withMembers(new ArrayList<>(g.getMembers()));
|
||||||
|
|
||||||
File aFile = getGroupAvatarFile(g.groupId);
|
File aFile = getGroupAvatarFile(g.getGroupId());
|
||||||
if (aFile.exists()) {
|
if (aFile.exists()) {
|
||||||
try {
|
try {
|
||||||
group.withAvatar(Utils.createAttachment(aFile));
|
group.withAvatar(Utils.createAttachment(aFile));
|
||||||
|
@ -978,10 +978,10 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<Long, List<SendMessageResult>> sendGroupInfoRequest(
|
Pair<Long, List<SendMessageResult>> sendGroupInfoRequest(
|
||||||
byte[] groupId, SignalServiceAddress recipient
|
GroupIdV1 groupId, SignalServiceAddress recipient
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO)
|
SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO)
|
||||||
.withId(groupId);
|
.withId(groupId.serialize());
|
||||||
|
|
||||||
SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
|
SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
|
||||||
.asGroupMessage(group.build());
|
.asGroupMessage(group.build());
|
||||||
|
@ -1087,7 +1087,7 @@ public class Manager implements Closeable {
|
||||||
account.save();
|
account.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGroupBlocked(final byte[] groupId, final boolean blocked) throws GroupNotFoundException {
|
public void setGroupBlocked(final GroupId groupId, final boolean blocked) throws GroupNotFoundException {
|
||||||
GroupInfo group = getGroup(groupId);
|
GroupInfo group = getGroup(groupId);
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
throw new GroupNotFoundException(groupId);
|
throw new GroupNotFoundException(groupId);
|
||||||
|
@ -1098,8 +1098,8 @@ public class Manager implements Closeable {
|
||||||
account.save();
|
account.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<byte[], List<SendMessageResult>> updateGroup(
|
public Pair<GroupId, List<SendMessageResult>> updateGroup(
|
||||||
byte[] groupId, String name, List<String> members, String avatar
|
GroupId groupId, String name, List<String> members, String avatar
|
||||||
) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException {
|
) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException {
|
||||||
return sendUpdateGroupMessage(groupId,
|
return sendUpdateGroupMessage(groupId,
|
||||||
name,
|
name,
|
||||||
|
@ -1137,7 +1137,7 @@ public class Manager implements Closeable {
|
||||||
/**
|
/**
|
||||||
* Change the expiration timer for a group
|
* Change the expiration timer for a group
|
||||||
*/
|
*/
|
||||||
public void setExpirationTimer(byte[] groupId, int messageExpirationTimer) {
|
public void setExpirationTimer(GroupId groupId, int messageExpirationTimer) {
|
||||||
GroupInfo g = account.getGroupStore().getGroup(groupId);
|
GroupInfo g = account.getGroupStore().getGroup(groupId);
|
||||||
if (g instanceof GroupInfoV1) {
|
if (g instanceof GroupInfoV1) {
|
||||||
GroupInfoV1 groupInfoV1 = (GroupInfoV1) g;
|
GroupInfoV1 groupInfoV1 = (GroupInfoV1) g;
|
||||||
|
@ -1551,20 +1551,21 @@ public class Manager implements Closeable {
|
||||||
if (message.getGroupContext().isPresent()) {
|
if (message.getGroupContext().isPresent()) {
|
||||||
if (message.getGroupContext().get().getGroupV1().isPresent()) {
|
if (message.getGroupContext().get().getGroupV1().isPresent()) {
|
||||||
SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
|
SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
|
||||||
GroupInfo group = account.getGroupStore().getGroupByV1Id(groupInfo.getGroupId());
|
GroupIdV1 groupId = GroupId.v1(groupInfo.getGroupId());
|
||||||
|
GroupInfo group = account.getGroupStore().getGroup(groupId);
|
||||||
if (group == null || group instanceof GroupInfoV1) {
|
if (group == null || group instanceof GroupInfoV1) {
|
||||||
GroupInfoV1 groupV1 = (GroupInfoV1) group;
|
GroupInfoV1 groupV1 = (GroupInfoV1) group;
|
||||||
switch (groupInfo.getType()) {
|
switch (groupInfo.getType()) {
|
||||||
case UPDATE: {
|
case UPDATE: {
|
||||||
if (groupV1 == null) {
|
if (groupV1 == null) {
|
||||||
groupV1 = new GroupInfoV1(groupInfo.getGroupId());
|
groupV1 = new GroupInfoV1(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupInfo.getAvatar().isPresent()) {
|
if (groupInfo.getAvatar().isPresent()) {
|
||||||
SignalServiceAttachment avatar = groupInfo.getAvatar().get();
|
SignalServiceAttachment avatar = groupInfo.getAvatar().get();
|
||||||
if (avatar.isPointer()) {
|
if (avatar.isPointer()) {
|
||||||
try {
|
try {
|
||||||
retrieveGroupAvatarAttachment(avatar.asPointer(), groupV1.groupId);
|
retrieveGroupAvatarAttachment(avatar.asPointer(), groupV1.getGroupId());
|
||||||
} catch (IOException | InvalidMessageException | MissingConfigurationException e) {
|
} catch (IOException | InvalidMessageException | MissingConfigurationException e) {
|
||||||
System.err.println("Failed to retrieve group avatar (" + avatar.asPointer()
|
System.err.println("Failed to retrieve group avatar (" + avatar.asPointer()
|
||||||
.getRemoteId() + "): " + e.getMessage());
|
.getRemoteId() + "): " + e.getMessage());
|
||||||
|
@ -1589,7 +1590,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
case DELIVER:
|
case DELIVER:
|
||||||
if (groupV1 == null && !isSync) {
|
if (groupV1 == null && !isSync) {
|
||||||
actions.add(new SendGroupInfoRequestAction(source, groupInfo.getGroupId()));
|
actions.add(new SendGroupInfoRequestAction(source, groupV1.getGroupId()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QUIT: {
|
case QUIT: {
|
||||||
|
@ -1601,7 +1602,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
case REQUEST_INFO:
|
case REQUEST_INFO:
|
||||||
if (groupV1 != null && !isSync) {
|
if (groupV1 != null && !isSync) {
|
||||||
actions.add(new SendGroupUpdateAction(source, groupV1.groupId));
|
actions.add(new SendGroupUpdateAction(source, groupV1.getGroupId()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1627,7 +1628,7 @@ public class Manager implements Closeable {
|
||||||
if (message.getGroupContext().isPresent()) {
|
if (message.getGroupContext().isPresent()) {
|
||||||
if (message.getGroupContext().get().getGroupV1().isPresent()) {
|
if (message.getGroupContext().get().getGroupV1().isPresent()) {
|
||||||
SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
|
SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
|
||||||
GroupInfoV1 group = account.getGroupStore().getOrCreateGroupV1(groupInfo.getGroupId());
|
GroupInfoV1 group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId()));
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
if (group.messageExpirationTime != message.getExpiresInSeconds()) {
|
if (group.messageExpirationTime != message.getExpiresInSeconds()) {
|
||||||
group.messageExpirationTime = message.getExpiresInSeconds();
|
group.messageExpirationTime = message.getExpiresInSeconds();
|
||||||
|
@ -1723,17 +1724,17 @@ public class Manager implements Closeable {
|
||||||
) {
|
) {
|
||||||
final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
||||||
|
|
||||||
byte[] groupId = groupSecretParams.getPublicParams().getGroupIdentifier().serialize();
|
GroupIdV2 groupId = GroupUtils.getGroupIdV2(groupSecretParams);
|
||||||
GroupInfo groupInfo = account.getGroupStore().getGroupByV2Id(groupId);
|
GroupInfo groupInfo = account.getGroupStore().getGroup(groupId);
|
||||||
final GroupInfoV2 groupInfoV2;
|
final GroupInfoV2 groupInfoV2;
|
||||||
if (groupInfo instanceof GroupInfoV1) {
|
if (groupInfo instanceof GroupInfoV1) {
|
||||||
// Received a v2 group message for a v1 group, we need to locally migrate the group
|
// Received a v2 group message for a v1 group, we need to locally migrate the group
|
||||||
account.getGroupStore().deleteGroup(groupInfo.groupId);
|
account.getGroupStore().deleteGroup(groupInfo.getGroupId());
|
||||||
groupInfoV2 = new GroupInfoV2(groupId, groupMasterKey);
|
groupInfoV2 = new GroupInfoV2(groupId, groupMasterKey);
|
||||||
System.err.println("Locally migrated group "
|
System.err.println("Locally migrated group "
|
||||||
+ Base64.encodeBytes(groupInfo.groupId)
|
+ groupInfo.getGroupId().toBase64()
|
||||||
+ " to group v2, id: "
|
+ " to group v2, id: "
|
||||||
+ Base64.encodeBytes(groupInfoV2.groupId)
|
+ groupInfoV2.getGroupId().toBase64()
|
||||||
+ " !!!");
|
+ " !!!");
|
||||||
} else if (groupInfo instanceof GroupInfoV2) {
|
} else if (groupInfo instanceof GroupInfoV2) {
|
||||||
groupInfoV2 = (GroupInfoV2) groupInfo;
|
groupInfoV2 = (GroupInfoV2) groupInfo;
|
||||||
|
@ -1970,19 +1971,14 @@ public class Manager implements Closeable {
|
||||||
if (content != null && content.getDataMessage().isPresent()) {
|
if (content != null && content.getDataMessage().isPresent()) {
|
||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
if (message.getGroupContext().isPresent()) {
|
if (message.getGroupContext().isPresent()) {
|
||||||
GroupInfo group = null;
|
|
||||||
if (message.getGroupContext().get().getGroupV1().isPresent()) {
|
if (message.getGroupContext().get().getGroupV1().isPresent()) {
|
||||||
SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
|
SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get();
|
||||||
if (groupInfo.getType() == SignalServiceGroup.Type.DELIVER) {
|
if (groupInfo.getType() != SignalServiceGroup.Type.DELIVER) {
|
||||||
group = getGroup(groupInfo.getGroupId());
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (message.getGroupContext().get().getGroupV2().isPresent()) {
|
GroupId groupId = GroupUtils.getGroupId(message.getGroupContext().get());
|
||||||
SignalServiceGroupV2 groupContext = message.getGroupContext().get().getGroupV2().get();
|
GroupInfo group = account.getGroupStore().getGroup(groupId);
|
||||||
final GroupMasterKey groupMasterKey = groupContext.getMasterKey();
|
|
||||||
byte[] groupId = GroupUtils.getGroupId(groupMasterKey);
|
|
||||||
group = account.getGroupStore().getGroupByV2Id(groupId);
|
|
||||||
}
|
|
||||||
if (group != null && group.isBlocked()) {
|
if (group != null && group.isBlocked()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2055,7 +2051,8 @@ public class Manager implements Closeable {
|
||||||
DeviceGroupsInputStream s = new DeviceGroupsInputStream(attachmentAsStream);
|
DeviceGroupsInputStream s = new DeviceGroupsInputStream(attachmentAsStream);
|
||||||
DeviceGroup g;
|
DeviceGroup g;
|
||||||
while ((g = s.read()) != null) {
|
while ((g = s.read()) != null) {
|
||||||
GroupInfoV1 syncGroup = account.getGroupStore().getOrCreateGroupV1(g.getId());
|
GroupInfoV1 syncGroup = account.getGroupStore()
|
||||||
|
.getOrCreateGroupV1(GroupId.v1(g.getId()));
|
||||||
if (syncGroup != null) {
|
if (syncGroup != null) {
|
||||||
if (g.getName().isPresent()) {
|
if (g.getName().isPresent()) {
|
||||||
syncGroup.name = g.getName().get();
|
syncGroup.name = g.getName().get();
|
||||||
|
@ -2076,7 +2073,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g.getAvatar().isPresent()) {
|
if (g.getAvatar().isPresent()) {
|
||||||
retrieveGroupAvatarAttachment(g.getAvatar().get(), syncGroup.groupId);
|
retrieveGroupAvatarAttachment(g.getAvatar().get(), syncGroup.getGroupId());
|
||||||
}
|
}
|
||||||
syncGroup.inboxPosition = g.getInboxPosition().orNull();
|
syncGroup.inboxPosition = g.getInboxPosition().orNull();
|
||||||
syncGroup.archived = g.isArchived();
|
syncGroup.archived = g.isArchived();
|
||||||
|
@ -2104,12 +2101,15 @@ public class Manager implements Closeable {
|
||||||
for (SignalServiceAddress address : blockedListMessage.getAddresses()) {
|
for (SignalServiceAddress address : blockedListMessage.getAddresses()) {
|
||||||
setContactBlocked(resolveSignalServiceAddress(address), true);
|
setContactBlocked(resolveSignalServiceAddress(address), true);
|
||||||
}
|
}
|
||||||
for (byte[] groupId : blockedListMessage.getGroupIds()) {
|
for (GroupId groupId : blockedListMessage.getGroupIds()
|
||||||
|
.stream()
|
||||||
|
.map(GroupId::unknownVersion)
|
||||||
|
.collect(Collectors.toSet())) {
|
||||||
try {
|
try {
|
||||||
setGroupBlocked(groupId, true);
|
setGroupBlocked(groupId, true);
|
||||||
} catch (GroupNotFoundException e) {
|
} catch (GroupNotFoundException e) {
|
||||||
System.err.println("BlockedListMessage contained groupID that was not found in GroupStore: "
|
System.err.println("BlockedListMessage contained groupID that was not found in GroupStore: "
|
||||||
+ Base64.encodeBytes(groupId));
|
+ groupId.toBase64());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2229,12 +2229,12 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getGroupAvatarFile(byte[] groupId) {
|
private File getGroupAvatarFile(GroupId groupId) {
|
||||||
return new File(pathConfig.getAvatarsPath(), "group-" + Base64.encodeBytes(groupId).replace("/", "_"));
|
return new File(pathConfig.getAvatarsPath(), "group-" + groupId.toBase64().replace("/", "_"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private File retrieveGroupAvatarAttachment(
|
private File retrieveGroupAvatarAttachment(
|
||||||
SignalServiceAttachment attachment, byte[] groupId
|
SignalServiceAttachment attachment, GroupId groupId
|
||||||
) throws IOException, InvalidMessageException, MissingConfigurationException {
|
) throws IOException, InvalidMessageException, MissingConfigurationException {
|
||||||
IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
|
IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
|
||||||
if (attachment.isPointer()) {
|
if (attachment.isPointer()) {
|
||||||
|
@ -2333,10 +2333,10 @@ public class Manager implements Closeable {
|
||||||
for (GroupInfo record : account.getGroupStore().getGroups()) {
|
for (GroupInfo record : account.getGroupStore().getGroups()) {
|
||||||
if (record instanceof GroupInfoV1) {
|
if (record instanceof GroupInfoV1) {
|
||||||
GroupInfoV1 groupInfo = (GroupInfoV1) record;
|
GroupInfoV1 groupInfo = (GroupInfoV1) record;
|
||||||
out.write(new DeviceGroup(groupInfo.groupId,
|
out.write(new DeviceGroup(groupInfo.getGroupId().serialize(),
|
||||||
Optional.fromNullable(groupInfo.name),
|
Optional.fromNullable(groupInfo.name),
|
||||||
new ArrayList<>(groupInfo.getMembers()),
|
new ArrayList<>(groupInfo.getMembers()),
|
||||||
createGroupAvatarAttachment(groupInfo.groupId),
|
createGroupAvatarAttachment(groupInfo.getGroupId()),
|
||||||
groupInfo.isMember(account.getSelfAddress()),
|
groupInfo.isMember(account.getSelfAddress()),
|
||||||
Optional.of(groupInfo.messageExpirationTime),
|
Optional.of(groupInfo.messageExpirationTime),
|
||||||
Optional.fromNullable(groupInfo.color),
|
Optional.fromNullable(groupInfo.color),
|
||||||
|
@ -2442,7 +2442,7 @@ public class Manager implements Closeable {
|
||||||
List<byte[]> groupIds = new ArrayList<>();
|
List<byte[]> groupIds = new ArrayList<>();
|
||||||
for (GroupInfo record : account.getGroupStore().getGroups()) {
|
for (GroupInfo record : account.getGroupStore().getGroups()) {
|
||||||
if (record.isBlocked()) {
|
if (record.isBlocked()) {
|
||||||
groupIds.add(record.groupId);
|
groupIds.add(record.getGroupId().serialize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendSyncMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(addresses, groupIds)));
|
sendSyncMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(addresses, groupIds)));
|
||||||
|
@ -2466,7 +2466,7 @@ public class Manager implements Closeable {
|
||||||
return account.getContactStore().getContact(Util.getSignalServiceAddressFromIdentifier(number));
|
return account.getContactStore().getContact(Util.getSignalServiceAddressFromIdentifier(number));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupInfo getGroup(byte[] groupId) {
|
public GroupInfo getGroup(GroupId groupId) {
|
||||||
return account.getGroupStore().getGroup(groupId);
|
return account.getGroupStore().getGroup(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package org.asamk.signal.manager;
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
import org.whispersystems.util.Base64;
|
|
||||||
|
|
||||||
public class NotAGroupMemberException extends Exception {
|
public class NotAGroupMemberException extends Exception {
|
||||||
|
|
||||||
public NotAGroupMemberException(byte[] groupId, String groupName) {
|
public NotAGroupMemberException(GroupId groupId, String groupName) {
|
||||||
super("User is not a member in group: " + groupName + " (" + Base64.encodeBytes(groupId) + ")");
|
super("User is not a member in group: " + groupName + " (" + groupId.toBase64() + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupIdV2;
|
||||||
import org.asamk.signal.manager.GroupLinkPassword;
|
import org.asamk.signal.manager.GroupLinkPassword;
|
||||||
|
import org.asamk.signal.manager.GroupUtils;
|
||||||
import org.asamk.signal.storage.groups.GroupInfoV2;
|
import org.asamk.signal.storage.groups.GroupInfoV2;
|
||||||
import org.asamk.signal.util.IOUtils;
|
import org.asamk.signal.util.IOUtils;
|
||||||
import org.signal.storageservice.protos.groups.AccessControl;
|
import org.signal.storageservice.protos.groups.AccessControl;
|
||||||
|
@ -117,7 +119,7 @@ public class GroupHelper {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final byte[] groupId = groupSecretParams.getPublicParams().getGroupIdentifier().serialize();
|
final GroupIdV2 groupId = GroupUtils.getGroupIdV2(groupSecretParams);
|
||||||
final GroupMasterKey masterKey = groupSecretParams.getMasterKey();
|
final GroupMasterKey masterKey = groupSecretParams.getMasterKey();
|
||||||
GroupInfoV2 g = new GroupInfoV2(groupId, masterKey);
|
GroupInfoV2 g = new GroupInfoV2(groupId, masterKey);
|
||||||
g.setGroup(decryptedGroup);
|
g.setGroup(decryptedGroup);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
import org.asamk.signal.storage.contacts.ContactInfo;
|
import org.asamk.signal.storage.contacts.ContactInfo;
|
||||||
import org.asamk.signal.storage.contacts.JsonContactsStore;
|
import org.asamk.signal.storage.contacts.JsonContactsStore;
|
||||||
import org.asamk.signal.storage.groups.GroupInfo;
|
import org.asamk.signal.storage.groups.GroupInfo;
|
||||||
|
@ -309,7 +310,7 @@ public class SignalAccount implements Closeable {
|
||||||
contactInfo.messageExpirationTime = thread.messageExpirationTime;
|
contactInfo.messageExpirationTime = thread.messageExpirationTime;
|
||||||
contactStore.updateContact(contactInfo);
|
contactStore.updateContact(contactInfo);
|
||||||
} else {
|
} else {
|
||||||
GroupInfo groupInfo = groupStore.getGroup(Base64.decode(thread.id));
|
GroupInfo groupInfo = groupStore.getGroup(GroupId.fromBase64(thread.id));
|
||||||
if (groupInfo instanceof GroupInfoV1) {
|
if (groupInfo instanceof GroupInfoV1) {
|
||||||
((GroupInfoV1) groupInfo).messageExpirationTime = thread.messageExpirationTime;
|
((GroupInfoV1) groupInfo).messageExpirationTime = thread.messageExpirationTime;
|
||||||
groupStore.updateGroup(groupInfo);
|
groupStore.updateGroup(groupInfo);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.asamk.signal.storage.groups;
|
package org.asamk.signal.storage.groups;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
import org.asamk.signal.manager.GroupInviteLinkUrl;
|
import org.asamk.signal.manager.GroupInviteLinkUrl;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
|
@ -12,12 +12,8 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
public abstract class GroupInfo {
|
public abstract class GroupInfo {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonIgnore
|
||||||
public final byte[] groupId;
|
public abstract GroupId getGroupId();
|
||||||
|
|
||||||
public GroupInfo(byte[] groupId) {
|
|
||||||
this.groupId = groupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public abstract String getTitle();
|
public abstract String getTitle();
|
||||||
|
|
|
@ -13,7 +13,11 @@ import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
|
import org.asamk.signal.manager.GroupIdV1;
|
||||||
|
import org.asamk.signal.manager.GroupIdV2;
|
||||||
import org.asamk.signal.manager.GroupInviteLinkUrl;
|
import org.asamk.signal.manager.GroupInviteLinkUrl;
|
||||||
|
import org.asamk.signal.manager.GroupUtils;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -26,8 +30,9 @@ public class GroupInfoV1 extends GroupInfo {
|
||||||
|
|
||||||
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
||||||
|
|
||||||
@JsonProperty
|
private final GroupIdV1 groupId;
|
||||||
public byte[] expectedV2Id;
|
|
||||||
|
private GroupIdV2 expectedV2Id;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
public String name;
|
public String name;
|
||||||
|
@ -47,18 +52,8 @@ public class GroupInfoV1 extends GroupInfo {
|
||||||
@JsonProperty(defaultValue = "false")
|
@JsonProperty(defaultValue = "false")
|
||||||
public boolean archived;
|
public boolean archived;
|
||||||
|
|
||||||
public GroupInfoV1(byte[] groupId) {
|
public GroupInfoV1(GroupIdV1 groupId) {
|
||||||
super(groupId);
|
this.groupId = groupId;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GroupInviteLinkUrl getGroupInviteLink() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupInfoV1(
|
public GroupInfoV1(
|
||||||
|
@ -74,8 +69,8 @@ public class GroupInfoV1 extends GroupInfo {
|
||||||
@JsonProperty("messageExpirationTime") int messageExpirationTime,
|
@JsonProperty("messageExpirationTime") int messageExpirationTime,
|
||||||
@JsonProperty("active") boolean _ignored_active
|
@JsonProperty("active") boolean _ignored_active
|
||||||
) {
|
) {
|
||||||
super(groupId);
|
this.groupId = GroupId.v1(groupId);
|
||||||
this.expectedV2Id = expectedV2Id;
|
this.expectedV2Id = GroupId.v2(expectedV2Id);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.members.addAll(members);
|
this.members.addAll(members);
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
@ -85,6 +80,40 @@ public class GroupInfoV1 extends GroupInfo {
|
||||||
this.messageExpirationTime = messageExpirationTime;
|
this.messageExpirationTime = messageExpirationTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@JsonIgnore
|
||||||
|
public GroupIdV1 getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("groupId")
|
||||||
|
private byte[] getGroupIdJackson() {
|
||||||
|
return groupId.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public GroupIdV2 getExpectedV2Id() {
|
||||||
|
if (expectedV2Id == null) {
|
||||||
|
expectedV2Id = GroupUtils.getGroupIdV2(groupId);
|
||||||
|
}
|
||||||
|
return expectedV2Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("expectedV2Id")
|
||||||
|
private byte[] getExpectedV2IdJackson() {
|
||||||
|
return expectedV2Id.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupInviteLinkUrl getGroupInviteLink() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public Set<SignalServiceAddress> getMembers() {
|
public Set<SignalServiceAddress> getMembers() {
|
||||||
return members;
|
return members;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.asamk.signal.storage.groups;
|
package org.asamk.signal.storage.groups;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupIdV2;
|
||||||
import org.asamk.signal.manager.GroupInviteLinkUrl;
|
import org.asamk.signal.manager.GroupInviteLinkUrl;
|
||||||
import org.signal.storageservice.protos.groups.AccessControl;
|
import org.signal.storageservice.protos.groups.AccessControl;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
|
@ -13,16 +14,22 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class GroupInfoV2 extends GroupInfo {
|
public class GroupInfoV2 extends GroupInfo {
|
||||||
|
|
||||||
|
private final GroupIdV2 groupId;
|
||||||
private final GroupMasterKey masterKey;
|
private final GroupMasterKey masterKey;
|
||||||
|
|
||||||
private boolean blocked;
|
private boolean blocked;
|
||||||
private DecryptedGroup group; // stored as a file with hexadecimal groupId as name
|
private DecryptedGroup group; // stored as a file with hexadecimal groupId as name
|
||||||
|
|
||||||
public GroupInfoV2(final byte[] groupId, final GroupMasterKey masterKey) {
|
public GroupInfoV2(final GroupIdV2 groupId, final GroupMasterKey masterKey) {
|
||||||
super(groupId);
|
this.groupId = groupId;
|
||||||
this.masterKey = masterKey;
|
this.masterKey = masterKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupIdV2 getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
public GroupMasterKey getMasterKey() {
|
public GroupMasterKey getMasterKey() {
|
||||||
return masterKey;
|
return masterKey;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
|
import org.asamk.signal.manager.GroupIdV1;
|
||||||
|
import org.asamk.signal.manager.GroupIdV2;
|
||||||
import org.asamk.signal.manager.GroupUtils;
|
import org.asamk.signal.manager.GroupUtils;
|
||||||
import org.asamk.signal.util.Hex;
|
import org.asamk.signal.util.Hex;
|
||||||
import org.asamk.signal.util.IOUtils;
|
import org.asamk.signal.util.IOUtils;
|
||||||
|
@ -25,7 +28,6 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -39,7 +41,7 @@ public class JsonGroupStore {
|
||||||
@JsonProperty("groups")
|
@JsonProperty("groups")
|
||||||
@JsonSerialize(using = GroupsSerializer.class)
|
@JsonSerialize(using = GroupsSerializer.class)
|
||||||
@JsonDeserialize(using = GroupsDeserializer.class)
|
@JsonDeserialize(using = GroupsDeserializer.class)
|
||||||
private final Map<String, GroupInfo> groups = new HashMap<>();
|
private final Map<GroupId, GroupInfo> groups = new HashMap<>();
|
||||||
|
|
||||||
private JsonGroupStore() {
|
private JsonGroupStore() {
|
||||||
}
|
}
|
||||||
|
@ -49,11 +51,11 @@ public class JsonGroupStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateGroup(GroupInfo group) {
|
public void updateGroup(GroupInfo group) {
|
||||||
groups.put(Base64.encodeBytes(group.groupId), group);
|
groups.put(group.getGroupId(), group);
|
||||||
if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() != null) {
|
if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() != null) {
|
||||||
try {
|
try {
|
||||||
IOUtils.createPrivateDirectories(groupCachePath);
|
IOUtils.createPrivateDirectories(groupCachePath);
|
||||||
try (FileOutputStream stream = new FileOutputStream(getGroupFile(group.groupId))) {
|
try (FileOutputStream stream = new FileOutputStream(getGroupFile(group.getGroupId()))) {
|
||||||
((GroupInfoV2) group).getGroup().writeTo(stream);
|
((GroupInfoV2) group).getGroup().writeTo(stream);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -62,57 +64,50 @@ public class JsonGroupStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteGroup(byte[] groupId) {
|
public void deleteGroup(GroupId groupId) {
|
||||||
groups.remove(Base64.encodeBytes(groupId));
|
groups.remove(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupInfo getGroup(byte[] groupId) {
|
public GroupInfo getGroup(GroupId groupId) {
|
||||||
final GroupInfo group = groups.get(Base64.encodeBytes(groupId));
|
GroupInfo group = groups.get(groupId);
|
||||||
if (group == null & groupId.length == 16) {
|
if (group == null) {
|
||||||
return getGroupByV1Id(groupId);
|
if (groupId instanceof GroupIdV1) {
|
||||||
|
group = groups.get(GroupUtils.getGroupIdV2((GroupIdV1) groupId));
|
||||||
|
} else if (groupId instanceof GroupIdV2) {
|
||||||
|
group = getGroupV1ByV2Id((GroupIdV2) groupId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loadDecryptedGroup(group);
|
loadDecryptedGroup(group);
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupInfo getGroupByV1Id(byte[] groupIdV1) {
|
private GroupInfoV1 getGroupV1ByV2Id(GroupIdV2 groupIdV2) {
|
||||||
GroupInfo group = groups.get(Base64.encodeBytes(groupIdV1));
|
|
||||||
if (group == null) {
|
|
||||||
group = groups.get(Base64.encodeBytes(GroupUtils.getGroupId(GroupUtils.deriveV2MigrationMasterKey(groupIdV1))));
|
|
||||||
}
|
|
||||||
loadDecryptedGroup(group);
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GroupInfo getGroupByV2Id(byte[] groupIdV2) {
|
|
||||||
GroupInfo group = groups.get(Base64.encodeBytes(groupIdV2));
|
|
||||||
if (group == null) {
|
|
||||||
for (GroupInfo g : groups.values()) {
|
for (GroupInfo g : groups.values()) {
|
||||||
if (g instanceof GroupInfoV1 && Arrays.equals(groupIdV2, ((GroupInfoV1) g).expectedV2Id)) {
|
if (g instanceof GroupInfoV1) {
|
||||||
group = g;
|
final GroupInfoV1 gv1 = (GroupInfoV1) g;
|
||||||
break;
|
if (groupIdV2.equals(gv1.getExpectedV2Id())) {
|
||||||
|
return gv1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadDecryptedGroup(group);
|
return null;
|
||||||
return group;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadDecryptedGroup(final GroupInfo group) {
|
private void loadDecryptedGroup(final GroupInfo group) {
|
||||||
if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) {
|
if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) {
|
||||||
try (FileInputStream stream = new FileInputStream(getGroupFile(group.groupId))) {
|
try (FileInputStream stream = new FileInputStream(getGroupFile(group.getGroupId()))) {
|
||||||
((GroupInfoV2) group).setGroup(DecryptedGroup.parseFrom(stream));
|
((GroupInfoV2) group).setGroup(DecryptedGroup.parseFrom(stream));
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getGroupFile(final byte[] groupId) {
|
private File getGroupFile(final GroupId groupId) {
|
||||||
return new File(groupCachePath, Hex.toStringCondensed(groupId));
|
return new File(groupCachePath, Hex.toStringCondensed(groupId.serialize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupInfoV1 getOrCreateGroupV1(byte[] groupId) {
|
public GroupInfoV1 getOrCreateGroupV1(GroupIdV1 groupId) {
|
||||||
GroupInfo group = groups.get(Base64.encodeBytes(groupId));
|
GroupInfo group = getGroup(groupId);
|
||||||
if (group instanceof GroupInfoV1) {
|
if (group instanceof GroupInfoV1) {
|
||||||
return (GroupInfoV1) group;
|
return (GroupInfoV1) group;
|
||||||
}
|
}
|
||||||
|
@ -146,7 +141,7 @@ public class JsonGroupStore {
|
||||||
} else if (group instanceof GroupInfoV2) {
|
} else if (group instanceof GroupInfoV2) {
|
||||||
final GroupInfoV2 groupV2 = (GroupInfoV2) group;
|
final GroupInfoV2 groupV2 = (GroupInfoV2) group;
|
||||||
jgen.writeStartObject();
|
jgen.writeStartObject();
|
||||||
jgen.writeStringField("groupId", Base64.encodeBytes(groupV2.groupId));
|
jgen.writeStringField("groupId", groupV2.getGroupId().toBase64());
|
||||||
jgen.writeStringField("masterKey", Base64.encodeBytes(groupV2.getMasterKey().serialize()));
|
jgen.writeStringField("masterKey", Base64.encodeBytes(groupV2.getMasterKey().serialize()));
|
||||||
jgen.writeBooleanField("blocked", groupV2.isBlocked());
|
jgen.writeBooleanField("blocked", groupV2.isBlocked());
|
||||||
jgen.writeEndObject();
|
jgen.writeEndObject();
|
||||||
|
@ -158,34 +153,31 @@ public class JsonGroupStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class GroupsDeserializer extends JsonDeserializer<Map<String, GroupInfo>> {
|
private static class GroupsDeserializer extends JsonDeserializer<Map<GroupId, GroupInfo>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, GroupInfo> deserialize(
|
public Map<GroupId, GroupInfo> deserialize(
|
||||||
JsonParser jsonParser, DeserializationContext deserializationContext
|
JsonParser jsonParser, DeserializationContext deserializationContext
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
Map<String, GroupInfo> groups = new HashMap<>();
|
Map<GroupId, GroupInfo> groups = new HashMap<>();
|
||||||
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
|
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
|
||||||
for (JsonNode n : node) {
|
for (JsonNode n : node) {
|
||||||
GroupInfo g;
|
GroupInfo g;
|
||||||
if (n.has("masterKey")) {
|
if (n.has("masterKey")) {
|
||||||
// a v2 group
|
// a v2 group
|
||||||
byte[] groupId = Base64.decode(n.get("groupId").asText());
|
GroupIdV2 groupId = GroupIdV2.fromBase64(n.get("groupId").asText());
|
||||||
try {
|
try {
|
||||||
GroupMasterKey masterKey = new GroupMasterKey(Base64.decode(n.get("masterKey").asText()));
|
GroupMasterKey masterKey = new GroupMasterKey(Base64.decode(n.get("masterKey").asText()));
|
||||||
g = new GroupInfoV2(groupId, masterKey);
|
g = new GroupInfoV2(groupId, masterKey);
|
||||||
} catch (InvalidInputException e) {
|
} catch (InvalidInputException e) {
|
||||||
throw new AssertionError("Invalid master key for group " + Base64.encodeBytes(groupId));
|
throw new AssertionError("Invalid master key for group " + groupId.toBase64());
|
||||||
}
|
}
|
||||||
g.setBlocked(n.get("blocked").asBoolean(false));
|
g.setBlocked(n.get("blocked").asBoolean(false));
|
||||||
} else {
|
} else {
|
||||||
GroupInfoV1 gv1 = jsonProcessor.treeToValue(n, GroupInfoV1.class);
|
GroupInfoV1 gv1 = jsonProcessor.treeToValue(n, GroupInfoV1.class);
|
||||||
if (gv1.expectedV2Id == null) {
|
|
||||||
gv1.expectedV2Id = GroupUtils.getGroupId(GroupUtils.deriveV2MigrationMasterKey(gv1.groupId));
|
|
||||||
}
|
|
||||||
g = gv1;
|
g = gv1;
|
||||||
}
|
}
|
||||||
groups.put(Base64.encodeBytes(g.groupId), g);
|
groups.put(g.getGroupId(), g);
|
||||||
}
|
}
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.asamk.signal.util;
|
package org.asamk.signal.util;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupIdFormatException;
|
||||||
import org.asamk.signal.manager.GroupNotFoundException;
|
import org.asamk.signal.manager.GroupNotFoundException;
|
||||||
import org.asamk.signal.manager.NotAGroupMemberException;
|
import org.asamk.signal.manager.NotAGroupMemberException;
|
||||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.asamk.signal.util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class GroupIdFormatException extends Exception {
|
|
||||||
|
|
||||||
public GroupIdFormatException(String groupId, IOException e) {
|
|
||||||
super("Failed to decode groupId (must be base64) \"" + groupId + "\": " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,13 +2,13 @@ package org.asamk.signal.util;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.GroupId;
|
||||||
|
import org.asamk.signal.manager.GroupIdFormatException;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
import org.whispersystems.util.Base64;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InvalidObjectException;
|
import java.io.InvalidObjectException;
|
||||||
|
|
||||||
public class Util {
|
public class Util {
|
||||||
|
@ -48,12 +48,8 @@ public class Util {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] decodeGroupId(String groupId) throws GroupIdFormatException {
|
public static GroupId decodeGroupId(String groupId) throws GroupIdFormatException {
|
||||||
try {
|
return GroupId.fromBase64(groupId);
|
||||||
return Base64.decode(groupId);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new GroupIdFormatException(groupId, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String canonicalizeNumber(String number, String localNumber) throws InvalidNumberException {
|
public static String canonicalizeNumber(String number, String localNumber) throws InvalidNumberException {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue