import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
-import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest;
-import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest.StickerInfo;
+import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload;
+import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload.StickerInfo;
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
-import org.whispersystems.signalservice.internal.push.StickerUploadAttributes;
-import org.whispersystems.signalservice.internal.push.StickerUploadAttributesResponse;
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
import org.whispersystems.signalservice.internal.util.Hex;
import org.whispersystems.util.Base64;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
return username;
}
- private SignalServiceAddress getSelfAddress() {
- return new SignalServiceAddress(null, username);
- }
-
private SignalServiceAccountManager getSignalServiceAccountManager() {
return new SignalServiceAccountManager(BaseConfig.serviceConfiguration, null, account.getUsername(), account.getPassword(), account.getDeviceId(), BaseConfig.USER_AGENT, timer);
}
}
private SignalServiceMessageReceiver getMessageReceiver() {
- return new SignalServiceMessageReceiver(BaseConfig.serviceConfiguration, null, username, account.getPassword(), account.getDeviceId(), account.getSignalingKey(), BaseConfig.USER_AGENT, null, timer);
+ return new SignalServiceMessageReceiver(BaseConfig.serviceConfiguration, account.getUuid(), account.getUsername(), account.getPassword(), account.getDeviceId(), account.getSignalingKey(), BaseConfig.USER_AGENT, null, timer);
}
private SignalServiceMessageSender getMessageSender() {
- return new SignalServiceMessageSender(BaseConfig.serviceConfiguration, null, username, account.getPassword(),
+ return new SignalServiceMessageSender(BaseConfig.serviceConfiguration, account.getUuid(), account.getUsername(), account.getPassword(),
account.getDeviceId(), account.getSignalProtocolStore(), BaseConfig.USER_AGENT, account.isMultiDevice(), Optional.fromNullable(messagePipe), Optional.fromNullable(unidentifiedMessagePipe), Optional.absent());
}
throw new GroupNotFoundException(groupId);
}
for (String member : g.members) {
- if (member.equals(this.username)) {
+ if (member.equals(account.getUsername())) {
return g;
}
}
@Override
public void sendGroupMessage(String messageText, List<String> attachments,
byte[] groupId)
- throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
+ throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException {
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
if (attachments != null) {
messageBuilder.withAttachments(Utils.getSignalServiceAttachments(attachments));
final GroupInfo g = getGroupForSending(groupId);
+ final Collection<SignalServiceAddress> membersSend = getSignalServiceAddresses(g.members);
// Don't send group message to ourself
- final List<String> membersSend = new ArrayList<>(g.members);
- membersSend.remove(this.username);
+ membersSend.remove(account.getSelfAddress());
sendMessageLegacy(messageBuilder, membersSend);
}
public void sendGroupMessageReaction(String emoji, boolean remove, SignalServiceAddress targetAuthor,
long targetSentTimestamp, byte[] groupId)
- throws IOException, EncapsulatedExceptions, AttachmentInvalidException {
+ throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, targetAuthor, targetSentTimestamp);
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
.withReaction(reaction)
messageBuilder.asGroupMessage(group);
}
final GroupInfo g = getGroupForSending(groupId);
+ final Collection<SignalServiceAddress> membersSend = getSignalServiceAddresses(g.members);
// Don't send group message to ourself
- final List<String> membersSend = new ArrayList<>(g.members);
- membersSend.remove(this.username);
+ membersSend.remove(account.getSelfAddress());
sendMessageLegacy(messageBuilder, membersSend);
}
- public void sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, EncapsulatedExceptions {
+ public void sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, EncapsulatedExceptions, InvalidNumberException {
SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT)
.withId(groupId)
.build();
.asGroupMessage(group);
final GroupInfo g = getGroupForSending(groupId);
- g.members.remove(this.username);
+ g.members.remove(account.getUsername());
account.getGroupStore().updateGroup(g);
- sendMessageLegacy(messageBuilder, g.members);
+ sendMessageLegacy(messageBuilder, getSignalServiceAddresses(g.members));
}
- private byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection<String> members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
+ private byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection<String> members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException {
GroupInfo g;
if (groupId == null) {
// Create new group
g = new GroupInfo(KeyUtils.createGroupId());
- g.members.add(username);
+ g.members.add(account.getUsername());
} else {
g = getGroupForSending(groupId);
}
if (members != null) {
Set<String> newMembers = new HashSet<>();
for (String member : members) {
- try {
- member = Utils.canonicalizeNumber(member, username);
- } catch (InvalidNumberException e) {
- System.err.println("Failed to add member \"" + member + "\" to group: " + e.getMessage());
- System.err.println("Aborting…");
- System.exit(1);
- }
+ member = Utils.canonicalizeNumber(member, account.getUsername());
if (g.members.contains(member)) {
continue;
}
SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(g);
+ final Collection<SignalServiceAddress> membersSend = getSignalServiceAddresses(g.members);
// Don't send group message to ourself
- final List<String> membersSend = new ArrayList<>(g.members);
- membersSend.remove(this.username);
+ membersSend.remove(account.getSelfAddress());
sendMessageLegacy(messageBuilder, membersSend);
return g.groupId;
}
- private void sendUpdateGroupMessage(byte[] groupId, String recipient) throws IOException, EncapsulatedExceptions {
+ private void sendUpdateGroupMessage(byte[] groupId, SignalServiceAddress recipient) throws IOException, EncapsulatedExceptions {
if (groupId == null) {
return;
}
GroupInfo g = getGroupForSending(groupId);
- if (!g.members.contains(recipient)) {
+ if (!g.members.contains(recipient.getNumber().get())) {
return;
}
SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(g);
// Send group message only to the recipient who requested it
- final List<String> membersSend = new ArrayList<>();
- membersSend.add(recipient);
- sendMessageLegacy(messageBuilder, membersSend);
+ sendMessageLegacy(messageBuilder, Collections.singleton(recipient));
}
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfo g) {
return messageBuilder;
}
- private void sendGroupInfoRequest(byte[] groupId, String recipient) throws IOException, EncapsulatedExceptions {
+ private void sendGroupInfoRequest(byte[] groupId, SignalServiceAddress recipient) throws IOException, EncapsulatedExceptions {
if (groupId == null) {
return;
}
}
// Send group info request message to the recipient who sent us a message with this groupId
- final List<String> membersSend = new ArrayList<>();
- membersSend.add(recipient);
- sendMessageLegacy(messageBuilder, membersSend);
+ sendMessageLegacy(messageBuilder, Collections.singleton(recipient));
}
@Override
public void sendMessage(String message, List<String> attachments, String recipient)
- throws EncapsulatedExceptions, AttachmentInvalidException, IOException {
+ throws EncapsulatedExceptions, AttachmentInvalidException, IOException, InvalidNumberException {
List<String> recipients = new ArrayList<>(1);
recipients.add(recipient);
sendMessage(message, attachments, recipients);
@Override
public void sendMessage(String messageText, List<String> attachments,
List<String> recipients)
- throws IOException, EncapsulatedExceptions, AttachmentInvalidException {
+ throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
if (attachments != null) {
List<SignalServiceAttachment> attachmentStreams = Utils.getSignalServiceAttachments(attachments);
messageBuilder.withAttachments(attachmentPointers);
}
messageBuilder.withProfileKey(account.getProfileKey().serialize());
- sendMessageLegacy(messageBuilder, recipients);
+ sendMessageLegacy(messageBuilder, getSignalServiceAddresses(recipients));
}
public void sendMessageReaction(String emoji, boolean remove, SignalServiceAddress targetAuthor,
long targetSentTimestamp, List<String> recipients)
- throws IOException, EncapsulatedExceptions, AttachmentInvalidException {
+ throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, targetAuthor, targetSentTimestamp);
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
.withReaction(reaction)
.withProfileKey(account.getProfileKey().serialize());
- sendMessageLegacy(messageBuilder, recipients);
+ sendMessageLegacy(messageBuilder, getSignalServiceAddresses(recipients));
}
@Override
- public void sendEndSessionMessage(List<String> recipients) throws IOException, EncapsulatedExceptions {
+ public void sendEndSessionMessage(List<String> recipients) throws IOException, EncapsulatedExceptions, InvalidNumberException {
SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
.asEndSessionMessage();
- sendMessageLegacy(messageBuilder, recipients);
+ sendMessageLegacy(messageBuilder, getSignalServiceAddresses(recipients));
}
@Override
public String getContactName(String number) throws InvalidNumberException {
- String canonicalizedNumber = Utils.canonicalizeNumber(number, username);
- ContactInfo contact = account.getContactStore().getContact(canonicalizedNumber);
+ String canonicalizedNumber = Utils.canonicalizeNumber(number, account.getUsername());
+ ContactInfo contact = account.getContactStore().getContact(new SignalServiceAddress(null, canonicalizedNumber));
if (contact == null) {
return "";
} else {
@Override
public void setContactName(String number, String name) throws InvalidNumberException {
- String canonicalizedNumber = Utils.canonicalizeNumber(number, username);
- ContactInfo contact = account.getContactStore().getContact(canonicalizedNumber);
+ String canonicalizedNumber = Utils.canonicalizeNumber(number, account.getUsername());
+ final SignalServiceAddress address = new SignalServiceAddress(null, canonicalizedNumber);
+ ContactInfo contact = account.getContactStore().getContact(address);
if (contact == null) {
- contact = new ContactInfo();
- contact.number = canonicalizedNumber;
+ contact = new ContactInfo(address);
System.err.println("Add contact " + canonicalizedNumber + " named " + name);
} else {
System.err.println("Updating contact " + canonicalizedNumber + " name " + contact.name + " -> " + name);
@Override
public void setContactBlocked(String number, boolean blocked) throws InvalidNumberException {
- number = Utils.canonicalizeNumber(number, username);
- ContactInfo contact = account.getContactStore().getContact(number);
+ number = Utils.canonicalizeNumber(number, account.getUsername());
+ final SignalServiceAddress address = new SignalServiceAddress(null, number);
+ ContactInfo contact = account.getContactStore().getContact(address);
if (contact == null) {
- contact = new ContactInfo();
- contact.number = number;
+ contact = new ContactInfo(address);
System.err.println("Adding and " + (blocked ? "blocking" : "unblocking") + " contact " + number);
} else {
System.err.println((blocked ? "Blocking" : "Unblocking") + " contact " + number);
}
@Override
- public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
+ public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException {
if (groupId.length == 0) {
groupId = null;
}
/**
* Change the expiration timer for a thread (number of groupId)
- *
- * @param numberOrGroupId
- * @param messageExpirationTimer
*/
public void setExpirationTimer(String numberOrGroupId, int messageExpirationTimer) {
ThreadInfo thread = account.getThreadStore().getThread(numberOrGroupId);
account.getThreadStore().updateThread(thread);
}
+ /**
+ * Upload the sticker pack from path.
+ *
+ * @param path Path can be a path to a manifest.json file or to a zip file that contains a manifest.json file
+ * @return if successful, returns the URL to install the sticker pack in the signal app
+ */
public String uploadStickerPack(String path) throws IOException, StickerPackInvalidException {
- JsonStickerPack pack = parseStickerPack(path);
+ SignalServiceStickerManifestUpload manifest = getSignalServiceStickerManifestUpload(path);
+
+ SignalServiceMessageSender messageSender = getMessageSender();
+
+ byte[] packKey = KeyUtils.createStickerUploadKey();
+ String packId = messageSender.uploadStickerManifest(manifest, packKey);
+
+ try {
+ return new URI("https", "signal.art", "/addstickers/", "pack_id=" + URLEncoder.encode(packId, "utf-8") + "&pack_key=" + URLEncoder.encode(Hex.toStringCondensed(packKey), "utf-8"))
+ .toString();
+ } catch (URISyntaxException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private SignalServiceStickerManifestUpload getSignalServiceStickerManifestUpload(final String path) throws IOException, StickerPackInvalidException {
+ ZipFile zip = null;
+ String rootPath = null;
+
+ final File file = new File(path);
+ if (file.getName().endsWith(".zip")) {
+ zip = new ZipFile(file);
+ } else if (file.getName().equals("manifest.json")) {
+ rootPath = file.getParent();
+ } else {
+ throw new StickerPackInvalidException("Could not find manifest.json");
+ }
+
+ JsonStickerPack pack = parseStickerPack(rootPath, zip);
if (pack.stickers == null) {
throw new StickerPackInvalidException("Must set a 'stickers' field.");
}
List<StickerInfo> stickers = new ArrayList<>(pack.stickers.size());
- for (int i = 0; i < pack.stickers.size(); i++) {
- if (pack.stickers.get(i).file == null) {
+ for (JsonStickerPack.JsonSticker sticker : pack.stickers) {
+ if (sticker.file == null) {
throw new StickerPackInvalidException("Must set a 'file' field on each sticker.");
}
- if (!stickerDataContainsPath(path, pack.stickers.get(i).file)) {
- throw new StickerPackInvalidException("Could not find find " + pack.stickers.get(i).file);
+
+ Pair<InputStream, Long> data;
+ try {
+ data = getInputStreamAndLength(rootPath, zip, sticker.file);
+ } catch (IOException ignored) {
+ throw new StickerPackInvalidException("Could not find find " + sticker.file);
}
- StickerInfo stickerInfo = new StickerInfo(i, Optional.fromNullable(pack.stickers.get(i).emoji).or(""));
+ StickerInfo stickerInfo = new StickerInfo(data.first(), data.second(), Optional.fromNullable(sticker.emoji).or(""));
stickers.add(stickerInfo);
}
- boolean uniqueCover = false;
- StickerInfo cover = stickers.get(0);
+ StickerInfo cover = null;
if (pack.cover != null) {
if (pack.cover.file == null) {
throw new StickerPackInvalidException("Must set a 'file' field on the cover.");
}
- if (!stickerDataContainsPath(path, pack.cover.file)) {
- throw new StickerPackInvalidException("Could not find find cover " + pack.cover.file);
+
+ Pair<InputStream, Long> data;
+ try {
+ data = getInputStreamAndLength(rootPath, zip, pack.cover.file);
+ } catch (IOException ignored) {
+ throw new StickerPackInvalidException("Could not find find " + pack.cover.file);
}
- uniqueCover = true;
- cover = new StickerInfo(pack.stickers.size(), Optional.fromNullable(pack.cover.emoji).or(""));
+ cover = new StickerInfo(data.first(), data.second(), Optional.fromNullable(pack.cover.emoji).or(""));
}
- SignalServiceStickerManifest manifest = new SignalServiceStickerManifest(
- Optional.fromNullable(pack.title).or(""),
- Optional.fromNullable(pack.author).or(""),
+ return new SignalServiceStickerManifestUpload(
+ pack.title,
+ pack.author,
cover,
stickers);
-
- SignalServiceMessageSender messageSender = new SignalServiceMessageSender(
- BaseConfig.serviceConfiguration,
- null,
- username,
- account.getPassword(),
- account.getDeviceId(),
- account.getSignalProtocolStore(),
- BaseConfig.USER_AGENT,
- account.isMultiDevice(),
- Optional.fromNullable(messagePipe),
- Optional.fromNullable(unidentifiedMessagePipe),
- Optional.<SignalServiceMessageSender.EventListener>absent());
-
- System.out.println("Starting upload process...");
- Pair<byte[], StickerUploadAttributesResponse> responsePair = messageSender.getStickerUploadAttributes(stickers.size() + (uniqueCover ? 1 : 0));
- byte[] packKey = responsePair.first();
- StickerUploadAttributesResponse response = responsePair.second();
-
- System.out.println("Uploading manifest...");
- messageSender.uploadStickerManifest(manifest, packKey, response.getManifest());
-
- Map<Integer, StickerUploadAttributes> attrById = new HashMap<>();
-
- for (StickerUploadAttributes attr : response.getStickers()) {
- attrById.put(attr.getId(), attr);
- }
-
- for (int i = 0; i < pack.stickers.size(); i++) {
- System.out.println("Uploading sticker " + (i+1) + "/" + pack.stickers.size() + "...");
- StickerUploadAttributes attr = attrById.get(i);
- if (attr == null) {
- throw new StickerPackInvalidException("Upload attributes missing for id " + i);
- }
-
- byte[] data = readStickerDataFromPath(path, pack.stickers.get(i).file);
- messageSender.uploadSticker(new ByteArrayInputStream(data), data.length, packKey, attr);
- }
-
- if (uniqueCover) {
- System.out.println("Uploading unique cover...");
- StickerUploadAttributes attr = attrById.get(pack.stickers.size());
- if (attr == null) {
- throw new StickerPackInvalidException("Upload attributes missing for cover with id " + pack.stickers.size());
- }
-
- byte[] data = readStickerDataFromPath(path, pack.cover.file);
- messageSender.uploadSticker(new ByteArrayInputStream(data), data.length, packKey, attr);
- }
-
- return "https://signal.art/addstickers/#pack_id=" + response.getPackId() + "&pack_key=" + Hex.toStringCondensed(packKey).replaceAll(" ", "");
}
- private static byte[] readStickerDataFromPath(String rootPath, String subFile) throws IOException, StickerPackInvalidException {
- if (rootPath.endsWith(".zip")) {
- ZipFile zip = new ZipFile(rootPath);
- ZipEntry entry = zip.getEntry(subFile);
- return IOUtils.readFully(zip.getInputStream(entry));
- } else if (rootPath.endsWith(".json")) {
- String dir = new File(rootPath).getParent();
- FileInputStream fis = new FileInputStream(new File(dir, subFile));
- return IOUtils.readFully(fis);
+ private static JsonStickerPack parseStickerPack(String rootPath, ZipFile zip) throws IOException {
+ InputStream inputStream;
+ if (zip != null) {
+ inputStream = zip.getInputStream(zip.getEntry("manifest.json"));
} else {
- throw new StickerPackInvalidException("Must point to either a ZIP or JSON file.");
+ inputStream = new FileInputStream((new File(rootPath, "manifest.json")));
}
+ return new ObjectMapper().readValue(inputStream, JsonStickerPack.class);
}
- private static boolean stickerDataContainsPath(String rootPath, String subFile) throws IOException {
- if (rootPath.endsWith(".zip")) {
- ZipFile zip = new ZipFile(rootPath);
- return zip.getEntry(subFile) != null;
- } else if (rootPath.endsWith(".json")) {
- String dir = new File(rootPath).getParent();
- return new File(dir, subFile).exists();
+ private static Pair<InputStream, Long> getInputStreamAndLength(final String rootPath, final ZipFile zip, final String subfile) throws IOException {
+ if (zip != null) {
+ final ZipEntry entry = zip.getEntry(subfile);
+ return new Pair<>(zip.getInputStream(entry), entry.getSize());
} else {
- return false;
+ final File file = new File(rootPath, subfile);
+ return new Pair<>(new FileInputStream(file), file.length());
}
}
- private static JsonStickerPack parseStickerPack(String rootPath) throws IOException, StickerPackInvalidException {
- if (!stickerDataContainsPath(rootPath, "manifest.json")) {
- throw new StickerPackInvalidException("Could not find manifest.json");
- }
-
- String json = new String(readStickerDataFromPath(rootPath, "manifest.json"));
-
- return new ObjectMapper().readValue(json, JsonStickerPack.class);
- }
-
private void requestSyncGroups() throws IOException {
SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder().setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS).build();
SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
}
private byte[] getTargetUnidentifiedAccessKey(SignalServiceAddress recipient) throws IOException {
- ContactInfo contact = account.getContactStore().getContact(recipient.getNumber().get());
+ ContactInfo contact = account.getContactStore().getContact(recipient);
if (contact == null || contact.profileKey == null) {
return null;
}
/**
* This method throws an EncapsulatedExceptions exception instead of returning a list of SendMessageResult.
*/
- private void sendMessageLegacy(SignalServiceDataMessage.Builder messageBuilder, Collection<String> recipients)
+ private void sendMessageLegacy(SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients)
throws EncapsulatedExceptions, IOException {
List<SendMessageResult> results = sendMessage(messageBuilder, recipients);
}
}
- private List<SendMessageResult> sendMessage(SignalServiceDataMessage.Builder messageBuilder, Collection<String> recipients)
- throws IOException {
- Set<SignalServiceAddress> recipientsTS = Utils.getSignalServiceAddresses(recipients, username);
- if (recipientsTS == null) {
- account.save();
- return Collections.emptyList();
+ private Collection<SignalServiceAddress> getSignalServiceAddresses(Collection<String> numbers) throws InvalidNumberException {
+ final Set<SignalServiceAddress> signalServiceAddresses = new HashSet<>(numbers.size());
+ final String username = account.getUsername();
+
+ for (String number : numbers) {
+ String canonicalizedNumber = Utils.canonicalizeNumber(number, username);
+ if (canonicalizedNumber.equals(username)) {
+ signalServiceAddresses.add(account.getSelfAddress());
+ } else {
+ // TODO get corresponding uuid
+ signalServiceAddresses.add(new SignalServiceAddress(null, canonicalizedNumber));
+ }
}
+ return signalServiceAddresses;
+ }
+ private List<SendMessageResult> sendMessage(SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients)
+ throws IOException {
if (messagePipe == null) {
messagePipe = getMessageReceiver().createMessagePipe();
}
if (message.getGroupInfo().isPresent()) {
try {
final boolean isRecipientUpdate = false;
- List<SendMessageResult> result = messageSender.sendMessage(new ArrayList<>(recipientsTS), getAccessFor(recipientsTS), isRecipientUpdate, message);
+ List<SendMessageResult> result = messageSender.sendMessage(new ArrayList<>(recipients), getAccessFor(recipients), isRecipientUpdate, message);
for (SendMessageResult r : result) {
if (r.getIdentityFailure() != null) {
account.getSignalProtocolStore().saveIdentity(r.getAddress().getNumber().get(), r.getIdentityFailure().getIdentityKey(), TrustLevel.UNTRUSTED);
account.getSignalProtocolStore().saveIdentity(e.getIdentifier(), e.getIdentityKey(), TrustLevel.UNTRUSTED);
return Collections.emptyList();
}
- } else if (recipientsTS.size() == 1 && recipientsTS.contains(getSelfAddress())) {
- SignalServiceAddress recipient = getSelfAddress();
+ } else if (recipients.size() == 1 && recipients.contains(account.getSelfAddress())) {
+ SignalServiceAddress recipient = account.getSelfAddress();
final Optional<UnidentifiedAccessPair> unidentifiedAccess = getAccessFor(recipient);
SentTranscriptMessage transcript = new SentTranscriptMessage(Optional.of(recipient),
message.getTimestamp(),
false);
SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript);
- List<SendMessageResult> results = new ArrayList<>(recipientsTS.size());
+ List<SendMessageResult> results = new ArrayList<>(recipients.size());
try {
messageSender.sendMessage(syncMessage, unidentifiedAccess);
} catch (UntrustedIdentityException e) {
return results;
} else {
// Send to all individually, so sync messages are sent correctly
- List<SendMessageResult> results = new ArrayList<>(recipientsTS.size());
- for (SignalServiceAddress address : recipientsTS) {
+ List<SendMessageResult> results = new ArrayList<>(recipients.size());
+ for (SignalServiceAddress address : recipients) {
ThreadInfo thread = account.getThreadStore().getThread(address.getNumber().get());
if (thread != null) {
messageBuilder.withExpiration(thread.messageExpirationTime);
}
} finally {
if (message != null && message.isEndSession()) {
- for (SignalServiceAddress recipient : recipientsTS) {
+ for (SignalServiceAddress recipient : recipients) {
handleEndSession(recipient.getNumber().get());
}
}
}
private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, ProtocolUntrustedIdentityException, SelfSendException, UnsupportedDataMessageException {
- SignalServiceCipher cipher = new SignalServiceCipher(getSelfAddress(), account.getSignalProtocolStore(), Utils.getCertificateValidator());
+ SignalServiceCipher cipher = new SignalServiceCipher(account.getSelfAddress(), account.getSignalProtocolStore(), Utils.getCertificateValidator());
try {
return cipher.decrypt(envelope);
} catch (ProtocolUntrustedIdentityException e) {
account.getSignalProtocolStore().deleteAllSessions(source);
}
- private void handleSignalServiceDataMessage(SignalServiceDataMessage message, boolean isSync, String source, SignalServiceAddress destination, boolean ignoreAttachments) {
+ private void handleSignalServiceDataMessage(SignalServiceDataMessage message, boolean isSync, SignalServiceAddress source, SignalServiceAddress destination, boolean ignoreAttachments) {
String threadId;
if (message.getGroupInfo().isPresent()) {
SignalServiceGroup groupInfo = message.getGroupInfo().get();
e.printStackTrace();
}
} else {
- group.members.remove(source);
+ group.members.remove(source.getNumber().get());
account.getGroupStore().updateGroup(group);
}
break;
if (isSync) {
threadId = destination.getNumber().get();
} else {
- threadId = source;
+ threadId = source.getNumber().get();
}
}
if (message.isEndSession()) {
- handleEndSession(isSync ? destination.getNumber().get() : source);
+ handleEndSession(isSync ? destination.getNumber().get() : source.getNumber().get());
}
if (message.isExpirationUpdate() || message.getBody().isPresent()) {
ThreadInfo thread = account.getThreadStore().getThread(threadId);
}
}
if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
- if (source.equals(username)) {
+ if (source.equals(account.getSelfAddress())) {
try {
this.account.setProfileKey(new ProfileKey(message.getProfileKey().get()));
} catch (InvalidInputException ignored) {
}
ContactInfo contact = account.getContactStore().getContact(source);
if (contact == null) {
- contact = new ContactInfo();
- contact.number = source;
+ contact = new ContactInfo(source);
}
contact.profileKey = Base64.encodeBytes(message.getProfileKey().get());
account.getContactStore().updateContact(contact);
}
if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get();
- handleSignalServiceDataMessage(message, false, sender.getNumber().get(), getSelfAddress(), ignoreAttachments);
+ handleSignalServiceDataMessage(message, false, sender, account.getSelfAddress(), ignoreAttachments);
}
if (content.getSyncMessage().isPresent()) {
account.setMultiDevice(true);
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
if (syncMessage.getSent().isPresent()) {
SentTranscriptMessage message = syncMessage.getSent().get();
- handleSignalServiceDataMessage(message.getMessage(), true, sender.getNumber().get(), message.getDestination().orNull(), ignoreAttachments);
+ handleSignalServiceDataMessage(message.getMessage(), true, sender, message.getDestination().orNull(), ignoreAttachments);
}
if (syncMessage.getRequest().isPresent()) {
RequestMessage rm = syncMessage.getRequest().get();
}
syncGroup.addMembers(g.getMembers());
if (!g.isActive()) {
- syncGroup.members.remove(username);
+ syncGroup.members.remove(account.getUsername());
+ } else {
+ // Add ourself to the member set as it's marked as active
+ syncGroup.members.add(account.getUsername());
}
syncGroup.blocked = g.isBlocked();
if (g.getColor().isPresent()) {
if (c.getAddress().matches(account.getSelfAddress()) && c.getProfileKey().isPresent()) {
account.setProfileKey(c.getProfileKey().get());
}
- ContactInfo contact = account.getContactStore().getContact(c.getAddress().getNumber().get());
+ ContactInfo contact = account.getContactStore().getContact(c.getAddress());
if (contact == null) {
- contact = new ContactInfo();
- contact.number = c.getAddress().getNumber().get();
+ contact = new ContactInfo(c.getAddress());
}
if (c.getName().isPresent()) {
contact.name = c.getName().get();
ThreadInfo info = account.getThreadStore().getThread(Base64.encodeBytes(record.groupId));
out.write(new DeviceGroup(record.groupId, Optional.fromNullable(record.name),
new ArrayList<>(record.getMembers()), createGroupAvatarAttachment(record.groupId),
- record.members.contains(username), Optional.fromNullable(info != null ? info.messageExpirationTime : null),
+ record.members.contains(account.getUsername()), Optional.fromNullable(info != null ? info.messageExpirationTime : null),
Optional.fromNullable(record.color), record.blocked, Optional.fromNullable(record.inboxPosition), record.archived));
}
}
}
public ContactInfo getContact(String number) {
- return account.getContactStore().getContact(number);
+ return account.getContactStore().getContact(new SignalServiceAddress(null, number));
}
public GroupInfo getGroup(byte[] groupId) {
}
public Pair<String, List<JsonIdentityKeyStore.Identity>> getIdentities(String number) throws InvalidNumberException {
- String canonicalizedNumber = Utils.canonicalizeNumber(number, username);
+ String canonicalizedNumber = Utils.canonicalizeNumber(number, account.getUsername());
return new Pair<>(canonicalizedNumber, account.getSignalProtocolStore().getIdentities(canonicalizedNumber));
}
}
public String computeSafetyNumber(String theirUsername, IdentityKey theirIdentityKey) {
- return Utils.computeSafetyNumber(username, getIdentity(), theirUsername, theirIdentityKey);
+ return Utils.computeSafetyNumber(account.getUsername(), getIdentity(), theirUsername, theirIdentityKey);
}
public interface ReceiveMessageHandler {